diff --git a/.github/workflows/block-remove-before-merge.yml b/.github/workflows/block-remove-before-merge.yml index 443eea9f7c..aedf2dfe4a 100644 --- a/.github/workflows/block-remove-before-merge.yml +++ b/.github/workflows/block-remove-before-merge.yml @@ -14,10 +14,9 @@ jobs: check-paths: name: "No remove-before-merge directories" if: github.event_name == 'pull_request' && github.base_ref == 'main' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04-2core-agent-runtime 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/sdk-dotnet.yml b/.github/workflows/sdk-dotnet.yml index 14a4c3265b..996570df9b 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: @@ -25,10 +24,9 @@ permissions: jobs: dotnet-linux-x64: - name: ".NET (ubuntu-latest, CAPI)" + name: ".NET (ubuntu-26.04-2core-agent-runtime, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linux-x64' - timeout-minutes: 30 - runs-on: ubuntu-latest + runs-on: ubuntu-26.04-2core-agent-runtime defaults: run: shell: bash @@ -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 @@ -93,7 +90,7 @@ jobs: - if: failure() uses: actions/upload-artifact@v7 with: - name: dotnet-test-diagnostics-ubuntu-latest-capi-${{ github.run_attempt }} + name: dotnet-test-diagnostics-ubuntu-26.04-2core-agent-runtime-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 @@ -102,7 +99,6 @@ jobs: name: ".NET (macos-26-xlarge-agent-runtime, default, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'darwin-arm64' runs-on: macos-26-xlarge-agent-runtime - timeout-minutes: 30 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-2025-4core-agent-runtime, ${{ matrix.framework }}, ${{ matrix.suite.name }}, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'win32-x64' - runs-on: windows-latest - timeout-minutes: 30 + runs-on: windows-2025-4core-agent-runtime + 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-2025-4core-agent-runtime-${{ matrix.framework }}-${{ matrix.suite.name }}-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 @@ -211,13 +216,11 @@ jobs: dotnet-musl-x64: name: ".NET (Alpine x64, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest - timeout-minutes: 20 + runs-on: ubuntu-26.04-2core-agent-runtime 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 b5593f26b1..b738a30972 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-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-26.04-2core-agent-runtime"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-2025-4core-agent-runtime"]' || '["ubuntu-26.04-2core-agent-runtime","macos-26-agent-runtime","windows-2025-4core-agent-runtime"]') }} 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'" @@ -101,11 +101,9 @@ jobs: go-musl-x64: name: "Go (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest - timeout-minutes: 20 + runs-on: ubuntu-26.04-2core-agent-runtime 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 011fef0898..9c43a3a2ed 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,12 +28,12 @@ jobs: strategy: fail-fast: false matrix: - 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"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-26.04-2core-agent-runtime"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-2025-4core-agent-runtime"]' || '["ubuntu-26.04-2core-agent-runtime","macos-26-agent-runtime","windows-2025-4core-agent-runtime"]') }} test-jdk: ["25", "17"] exclude: - os: macos-26-agent-runtime test-jdk: "17" - - os: windows-latest + - os: windows-2025-4core-agent-runtime test-jdk: "17" runs-on: ${{ matrix.os }} defaults: @@ -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 @@ -125,11 +123,9 @@ jobs: java-musl-x64: name: "Java (JDK 25, linuxmusl-x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest - timeout-minutes: 30 + runs-on: ubuntu-26.04-2core-agent-runtime 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 db14685c13..6dcd12c354 100644 --- a/.github/workflows/sdk-nodejs.yml +++ b/.github/workflows/sdk-nodejs.yml @@ -1,13 +1,12 @@ # Invoked by sdk.yml to run Node.js SDK builds, tests, packaging checks, # documentation validation, full subprocess coverage, and focused in-process -# smoke coverage on standard platforms and x64 musl. +# smoke coverage on standard platforms and x64 musl, plus full in-process E2Es on Linux. name: "SDK Node.js" 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-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-26.04-4core-agent-runtime"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-2025-4core-agent-runtime"]' || '["ubuntu-26.04-4core-agent-runtime","macos-26-agent-runtime","windows-2025-4core-agent-runtime"]') }} 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 @@ -93,6 +91,7 @@ jobs: id: subprocess env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + VITEST_MAX_WORKERS: ${{ runner.os == 'Linux' && '4' || '' }} run: | 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" @@ -122,13 +121,48 @@ jobs: npm run extract npm run validate:ts + # Keep full in-process coverage separate from the required CAPI result. + nodejs-inprocess: + name: "Node.js (ubuntu-26.04-4core-agent-runtime, in-process, CAPI)" + if: github.event_name != 'merge_group' && (inputs.platform == 'all' || inputs.platform == 'linux-x64') + runs-on: ubuntu-26.04-4core-agent-runtime + defaults: + run: + shell: bash + working-directory: ${{ inputs.sdk-home }}/nodejs + env: + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + VITEST_MAX_WORKERS: 4 + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: ./.github/actions/download-sdk-runtime + with: + artifact-name: executable-sdk-Linux-X64-gnu + path: ${{ github.workspace }} + - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare + working-directory: . + - uses: actions/setup-node@v6 + with: + node-version: 22 + cache: npm + cache-dependency-path: ${{ inputs.sdk-home }}/nodejs/package-lock.json + - run: npm ci --ignore-scripts + - run: npm run build + - run: npm ci --ignore-scripts + working-directory: ${{ inputs.sdk-home }}/test/harness + - name: Test in-process E2Es + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + run: npm test -- test/e2e + # Keep each BYOK backend on its own runner; shared runtime E2E coverage belongs # in TypeScript rather than repeating these sweeps across SDK languages. nodejs-backends: - name: "Node.js (ubuntu-latest, ${{ matrix.backend }})" + name: "Node.js (ubuntu-26.04-2core-agent-runtime, ${{ matrix.backend }})" if: github.event_name != 'merge_group' && (inputs.platform == 'all' || inputs.platform == 'linux-x64') - runs-on: ubuntu-latest - timeout-minutes: 30 + runs-on: ubuntu-26.04-2core-agent-runtime strategy: fail-fast: false matrix: @@ -141,7 +175,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 @@ -167,10 +200,9 @@ jobs: nodejs-musl-x64: name: "Node.js (Alpine x64, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04-2core-agent-runtime steps: - uses: actions/checkout@v7 - timeout-minutes: 4 with: persist-credentials: false - uses: ./.github/actions/download-sdk-runtime @@ -187,6 +219,7 @@ jobs: sdk-root: /workspace/${{ inputs.sdk-home }} workdir: /workspace/${{ inputs.sdk-home }}/nodejs command: | + apk add --no-cache git-daemon npm ci --ignore-scripts (cd "$COPILOT_SDK_ROOT/scripts/codegen" && npm ci --ignore-scripts) npm run build diff --git a/.github/workflows/sdk-python.yml b/.github/workflows/sdk-python.yml index df9cbab1a9..c04c8a6805 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-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-agent-runtime","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-26.04-2core-agent-runtime"]' || inputs.platform == 'darwin-arm64' && '["macos-26-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-2025-4core-agent-runtime"]' || '["ubuntu-26.04-2core-agent-runtime","macos-26-agent-runtime","windows-2025-4core-agent-runtime"]') }} 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 @@ -104,11 +101,9 @@ jobs: python-musl-x64: name: "Python (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest - timeout-minutes: 20 + runs-on: ubuntu-26.04-2core-agent-runtime 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..10baa49617 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 @@ -143,8 +137,7 @@ jobs: !cancelled() && (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 }} + runs-on: ${{ inputs.runtime-source == 'checkout' && inputs.os || 'ubuntu-26.04-2core-agent-runtime' }} 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 04500c3489..86c16d671b 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-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-xlarge-agent-runtime","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-26.04-2core-agent-runtime"]' || inputs.platform == 'darwin-arm64' && '["macos-26-xlarge-agent-runtime"]' || inputs.platform == 'win32-x64' && '["windows-2025-4core-agent-runtime"]' || '["ubuntu-26.04-2core-agent-runtime","macos-26-xlarge-agent-runtime","windows-2025-4core-agent-runtime"]') }} 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 @@ -127,11 +127,9 @@ jobs: rust-musl-x64: name: "Rust (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest - timeout-minutes: 60 + runs-on: ubuntu-26.04-2core-agent-runtime 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 602d02b027..8a77f8729d 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. @@ -171,15 +169,20 @@ jobs: name: "Detect SDK layout" needs: detect-sdk-changes if: ${{ !cancelled() && (needs.detect-sdk-changes.result != 'success' || needs.detect-sdk-changes.outputs.has_sdk_changes == 'true') }} - runs-on: ubuntu-latest + runs-on: ubuntu-26.04-2core-agent-runtime outputs: runtime-source: ${{ steps.detect.outputs.runtime-source }} 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: @@ -347,12 +348,11 @@ jobs: with: runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - os: ubuntu-latest-xl + os: ubuntu-26.04-16core-agent-runtime target: linux-x64 artifact: Linux-X64-gnu bazel-config: e2e-no-lto shared-cli-build-compatible: true - timeout-minutes: 60 secrets: inherit build-runtime-linuxmusl-x64: @@ -368,11 +368,10 @@ jobs: with: runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - os: ubuntu-latest-xl + os: ubuntu-26.04-16core-agent-runtime target: linuxmusl-x64 artifact: Linux-X64-musl bazel-config: e2e-no-lto - timeout-minutes: 60 secrets: inherit build-runtime-linuxmusl-arm64: @@ -388,11 +387,10 @@ jobs: with: runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - os: ubuntu-latest-xl + os: ubuntu-26.04-16core-agent-runtime target: linuxmusl-arm64 artifact: Linux-ARM64-musl bazel-config: e2e-no-lto - timeout-minutes: 60 secrets: inherit build-runtime-darwin-arm64: @@ -414,7 +412,6 @@ jobs: 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: @@ -430,13 +427,12 @@ jobs: with: runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - os: windows-2025-16core + os: windows-2025-16core-agent-runtime target: win32-x64 artifact: Windows-X64 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: @@ -522,19 +518,40 @@ jobs: check-freshness: name: "Check schema and SDK freshness" needs: detect-layout - runs-on: ubuntu-latest + runs-on: ubuntu-26.04-2core-agent-runtime permissions: contents: read 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..527e72ee70 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -356,18 +356,17 @@ 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 -its build, packaging, and applicable static checks. Other platforms, BYOK -backends, and languages keep their existing scheduling and failure reporting; -they do not gate this rollup. The full `SDK` aggregate still requires all -scheduled coverage to succeed. +`sdk-typescript` gates only the Linux CAPI subprocess job's build, packaging, +tests, and static checks. Full TypeScript in-process E2Es run on Linux/glibc x64 +outside merge groups. They, other platforms, BYOK backends, and languages do not +gate this rollup; the full `SDK` aggregate requires all scheduled coverage to succeed. The three BYOK backend sweeps run in separate Linux TypeScript jobs, alongside the normal CAPI job; they do not repeat unit tests, packaging, or static diff --git a/README.md b/README.md index 1ae92a18a9..676cf4ea80 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,21 @@ Your Application The SDK manages the CLI process lifecycle automatically. You can also connect to an external CLI server—see the [Getting Started Guide](./docs/getting-started.md#connecting-to-an-external-cli-server) for details on running the CLI in server mode. +### Portable elicitation schemas + +For form elicitation callbacks, the portable schema contract is an object form +with modeled `properties` and optional `required` fields (`type: "object"` on +the wire; an SDK may represent the object type implicitly). Applications that +support multiple SDK languages must use the modeled property definitions and +must not depend on additional schema metadata (such as a top-level `title`) or +on the insertion order of `properties`. + +Node.js and Rust pass through the wire schema, including additional metadata +and property order. Python, Go, .NET, and Java reconstruct the callback schema +from the modeled fields and do not offer that same preservation guarantee. +Treat wire-schema preservation as a language-specific capability, not a +cross-language requirement. + ### Agent Finder Skill installation APIs Runtimes that grant `skill-confirmed-installation` expose 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/dotnet/README.md b/dotnet/README.md index 84f5b650f1..60634a2c27 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -229,11 +229,13 @@ Create a new conversation session. - `InfiniteSessions` - Configure automatic context compaction (see below) - `WorkingDirectory` - Working directory for the session. When not set, the runtime uses its own process working directory. - `EnableSessionStore` - Enables the cross-session store for search and retrieval across sessions. When unset in `CopilotClientMode.CopilotCli`, the runtime default applies (enabled). In `CopilotClientMode.Empty`, defaults to disabled. +- `EnableGitHubCacheStore` - Requests the read-only GitHub cache store (`github-cache-store` capability) on create or resume. The runtime registers the `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is also on. Defaults to disabled in every mode. - `GitHubTokenProvider` - Acquires session-scoped GitHub tokens on demand. Return `GitHubTokenProviderResult.FromToken` with a positive `ExpiresIn` value (production GitHub tokens typically use `8 * 60 * 60` seconds), or `GitHubTokenProviderResult.Cancel()`. Cannot be combined with `GitHubToken`. - `SkillProvider` - Experimental session-scoped skill provider. See [Skill providers (experimental)](#skill-providers-experimental). - `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `PermissionHandler.ApproveAll` approves requests when managed settings are disabled and throws when `EnableManagedSettings` is true. Custom handlers can inspect `ManagedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` - Handler for legacy question-and-answer requests from the agent. Enables the legacy `ask_user` tool. See [User Input Requests](#user-input-requests) section. - `AskUserVariant` - Selects the model-facing `ask_user` tool shape. Defaults to `AskUserVariant.Legacy`; use `AskUserVariant.Elicitation` with `OnElicitationRequest`. +- `InteractiveUser` - Declares that a person types this session's messages, as in an editor chat panel. Enables the runtime's user-message analysis telemetry for user-authored messages; automated messages stay excluded. Defaults to off. Ignored for cloud sessions. - `Hooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. - `CanvasHandler` - Handles canvas open, close, and action callbacks. The SDK awaits asynchronous callbacks before replying, including callbacks without a result, unless @@ -251,6 +253,7 @@ Resume an existing session. Returns the session with `WorkspacePath` populated i - `GitHubTokenProvider` - Replaces the session-scoped token provider when resuming. Cannot be combined with `GitHubToken`. - `SkillProvider` - Re-supplies the session-scoped skill provider when resuming. - `AskUserVariant` - Re-supplies the model-facing `ask_user` tool shape on cold resume. +- `InteractiveUser` - Re-supplies the interactive-user declaration on cold resume. It is ignored when resuming a session that is already active. - `AllowTranscriptRecovery` - Repairs a damaged transcript when true. The default is true in all modes; set false to reject recovery. `session.TranscriptRecovery` contains `PlannedBackupPath`, `InvalidLineNumbers` (including torn-tail loss), and diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index ad75548f77..2b09e80f7e 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1262,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, @@ -1304,6 +1305,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.EnableFileHooks, config.EnableHostGitOperations, config.EnableSessionStore, + config.EnableGitHubCacheStore, config.EnableSkills, config.SkillDirectories, config.DisabledSkills, @@ -1321,6 +1323,8 @@ 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, @@ -1340,6 +1344,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, EnforceManagedModelDefaults: config.EnforceManagedModelDefaults, + InteractiveUser: config.InteractiveUser, GitHubMcpToolConfig: config.GitHubMcpToolConfig, ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, @@ -1567,6 +1572,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.EnableFileHooks, config.EnableHostGitOperations, config.EnableSessionStore, + config.EnableGitHubCacheStore, config.EnableSkills, config.SuppressResumeEvent is true ? true : null, config.Streaming is true ? true : null, @@ -1594,6 +1600,8 @@ 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, @@ -1614,6 +1622,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, EnforceManagedModelDefaults: config.EnforceManagedModelDefaults, + InteractiveUser: config.InteractiveUser, GitHubMcpToolConfig: config.GitHubMcpToolConfig, ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, @@ -3224,6 +3233,7 @@ public static string Escape(string arg) internal record CreateSessionRequest( string? Model, string? SessionId, + string? Name, string? ClientName, string? ReasoningEffort, ReasoningSummary? ReasoningSummary, @@ -3266,6 +3276,7 @@ internal record CreateSessionRequest( bool? EnableFileHooks, bool? EnableHostGitOperations, bool? EnableSessionStore, + [property: JsonPropertyName("enableGitHubCacheStore")] bool? EnableGitHubCacheStore, bool? EnableSkills, IList? SkillDirectories, IList? DisabledSkills, @@ -3283,6 +3294,8 @@ 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, @@ -3303,6 +3316,7 @@ internal record CreateSessionRequest( [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, [property: JsonPropertyName("enforceManagedModelDefaults")] bool? EnforceManagedModelDefaults = null, + [property: JsonPropertyName("interactiveUser")] bool? InteractiveUser = null, [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, @@ -3379,6 +3393,7 @@ internal record ResumeSessionRequest( bool? EnableFileHooks, bool? EnableHostGitOperations, bool? EnableSessionStore, + [property: JsonPropertyName("enableGitHubCacheStore")] bool? EnableGitHubCacheStore, bool? EnableSkills, [property: JsonPropertyName("disableResume")] bool? SuppressResumeEvent, bool? Streaming, @@ -3406,6 +3421,8 @@ 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, @@ -3427,6 +3444,7 @@ internal record ResumeSessionRequest( [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, [property: JsonPropertyName("enforceManagedModelDefaults")] bool? EnforceManagedModelDefaults = null, + [property: JsonPropertyName("interactiveUser")] bool? InteractiveUser = null, [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index a9b486d94c..c7f7973d96 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -107,2265 +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; } -/// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class 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. - [JsonPropertyName("excludeGhReplaceableTools")] - public bool ExcludeGhReplaceableTools { 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; } } -/// The session tool filters that decide whether the session still has a shell to run `gh` with. +/// 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)] -internal sealed class McpShouldExcludeGitHubToolsRequest +public sealed class ManagedSettingsValues { - /// The session's tool allowlist, when it set one. Omitted means the session constrains nothing this way. - [JsonPropertyName("availableTools")] - public IList? AvailableTools { get; set; } + /// Managed Auto routing preference, used when the selected model is `auto`. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } - /// The session's tool denylist, when it set one. Omitted means the session constrains nothing this way. - [JsonPropertyName("excludedTools")] - public IList? ExcludedTools { get; set; } + /// Managed context-tier default for the managed concrete model. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } - /// 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; } + /// 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; } + + /// 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; } } -/// 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. +/// Effective enterprise managed settings for an account, resolved without a session. [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 ManagedSettingsResolveResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// 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; } + /// 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; } -/// 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; } + /// 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; } - /// Protocol version of the runtime that served the request. - [JsonPropertyName("runtimeProtocolVersion")] - public long RuntimeProtocolVersion { 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; } + + /// 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; } } -/// 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. +/// RPC data type for ManagedSettingsResolve operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanConfigurationChange +internal sealed class ManagedSettingsResolveRequest { - /// Names of the configuration fields the change would set, without their values. - [JsonPropertyName("changedFields")] - public IList ChangedFields { get => field ??= []; 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; + /// Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// Whether the change would create a new entry or modify an existing one. - [JsonPropertyName("operation")] - public McpPlanConfigurationOperation Operation { 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; } - /// Scope the change would be written to. - [JsonPropertyName("scope")] - public McpPlanScope Scope { 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; } - /// 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; } + /// 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; } } -/// Normalised identity of the MCP server a plan targets, independent of how the card spelled it. +/// The authoring JSON schema for managed settings recognized by this runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanResourceIdentity +public sealed class ManagedSettingsSchemaResult { - /// 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 of the runtime that owns this schema. + [JsonPropertyName("runtimeVersion")] + public string RuntimeVersion { get; set; } = string.Empty; - /// Version advertised by the card, when it declares one. - [JsonPropertyName("version")] - public string? Version { 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; } } -/// Outcome of evaluating the planned server against registry and enterprise policy. Evaluation is read-only. +/// Result of validating a managed-settings document. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanPolicyResult +public sealed class ManagedSettingsValidateResult { - /// What policy decided for this server. - [JsonPropertyName("decision")] - public McpPlanPolicyDecision Decision { get; set; } + /// Errors that reject the document and warnings about content the runtime ignores. + [JsonPropertyName("diagnostics")] + public IList Diagnostics { 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("reason")] - public string? Reason { 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; } - /// Which authority produced the decision. - [JsonPropertyName("source")] - public McpPlanPolicySource Source { 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")] @@ -2373,2455 +2491,2196 @@ 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"; - - /// Where the value is applied when the server is launched. - [JsonPropertyName("category")] - public required McpPlanValueCategory Category { get; set; } + /// Advertised Agent Host Protocol version. + [JsonPropertyName("ahpVersion")] + public string? AhpVersion { 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; } + /// Current session count, when advertised. + [JsonPropertyName("currentSessions")] + public long? CurrentSessions { 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; } + /// Feature identifiers advertised by the environment. + [JsonPropertyName("features")] + public IList Features { get => field ??= []; set; } - /// Non-empty permitted value set. Inert untrusted data. - [JsonPropertyName("enumValues")] - public required IList EnumValues { get; set; } + /// Maximum session capacity, when advertised. + [JsonPropertyName("maxSessions")] + public long? MaxSessions { get; set; } +} - /// Whether the value may be supplied more than once. - [JsonPropertyName("isRepeated")] - public required bool IsRepeated { 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; } - - /// Whether the value must be present for the plan to be applicable. - [JsonPropertyName("required")] - public required bool Required { 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; } - - /// Discriminator: the value must be one of `enumValues`. - [JsonPropertyName("valueType")] - public required McpPlanEnumValueType ValueType { get; set; } -} - -/// 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. +/// Safe discovery information. Host-side relay bootstrap credentials are never included. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanSecretPlaceholder +public sealed class GitHubEnvironment { - /// 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; + /// Hosting capabilities advertised by the environment. + [JsonPropertyName("capabilities")] + public EnvironmentCapabilities? Capabilities { 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 assigned by Mission Control. + [JsonPropertyName("id")] + public string Id { 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; } -} + /// Compute kind reported by Mission Control. + [JsonPropertyName("kind")] + public EnvironmentKind Kind { get; set; } -/// 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"; + /// Discovery labels attached to the environment. + [JsonPropertyName("labels")] + public IDictionary? Labels { 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; } + /// Timestamp of the last heartbeat received by Mission Control. + [JsonPropertyName("lastHeartbeatAt")] + public string? LastHeartbeatAt { 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; } + /// Human-readable environment name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// 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; } + /// Organization identifier, when the environment belongs to an organization. + [JsonPropertyName("orgId")] + public string? OrgId { get; set; } - /// Typed values this choice requires, excluding secrets. - [JsonPropertyName("requiredValues")] - public required IList RequiredValues { get; set; } + /// Identifier of the environment owner. + [JsonPropertyName("ownerId")] + public string? OwnerId { get; set; } - /// Secrets this choice requires, referenced by placeholder only. - [JsonPropertyName("secretPlaceholders")] - public required IList SecretPlaceholders { get; set; } + /// Owner category reported by Mission Control. + [JsonPropertyName("ownerType")] + public string? OwnerType { get; set; } - /// Local process transport this package choice would use. - [JsonPropertyName("transport")] - public required McpPlanPackageTransport Transport { get; set; } + /// Open-ended operational status vocabulary. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; } -/// An eligible remote-endpoint transport choice. The endpoint is required and package identity cannot be represented. -/// The remote variant of . +/// Environments visible to the authenticated caller and matching the supplied filters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanTransportChoiceRemote : McpPlanTransportChoice +public sealed class EnvironmentsListResult { - /// - [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; } - - /// 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; } - - /// 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; } - - /// Endpoint transport this remote choice would use. - [JsonPropertyName("transport")] - public required McpPlanRemoteTransport Transport { get; set; } + /// Safe discovery records without relay bootstrap credentials. + [JsonPropertyName("environments")] + public IList Environments { get => field ??= []; set; } } -/// A normalised, inert description of what installing an MCP server would involve. Carries no raw card, no install specification, and no secret value. +/// Optional discovery filters supported by GitHub Mission Control. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallPlan +internal sealed class EnvironmentsListRequest { - /// 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; } - - /// Normalised identity of the server the plan would install. - [JsonPropertyName("identity")] - public McpPlanResourceIdentity Identity { get => field ??= new(); set; } - - /// 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; - - /// 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; + /// Restrict discovery to this compute kind. + [JsonPropertyName("kind")] + public EnvironmentKind? Kind { get; set; } - /// Outcome of evaluating the server against registry and enterprise policy. - [JsonPropertyName("policy")] - public McpPlanPolicyResult Policy { get => field ??= new(); set; } + /// Operational status, such as online, offline, degraded, waking, or draining. + [JsonPropertyName("status")] + public string? Status { 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; } +/// 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; } +} - /// 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; } +/// 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; +} - /// Whether applying this plan would require an MCP reload to take effect. Planning itself never reloads. - [JsonPropertyName("reloadRequired")] - public bool ReloadRequired { get; set; } +/// Acknowledgement that the requested environment was deleted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class EnvironmentsDeleteResult +{ +} - /// 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; } +/// Identify a user-managed Mission Control environment to delete. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class EnvironmentsDeleteRequest +{ + /// User-managed environment to delete. GitHub-managed environments cannot be deleted. + [JsonPropertyName("environmentId")] + public string EnvironmentId { get; set; } = string.Empty; +} - /// Configuration scope and key the plan would write to. - [JsonPropertyName("target")] - public McpPlanTarget Target { 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; - /// 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; } + /// AHP resource URI for the session on this listener. + [JsonPropertyName("sessionUri")] + public string SessionUri { 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 . +/// Publishes a resident session attached to the listener's owning connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultPlanned : McpPlanInstallResult +internal sealed class HostPublishSessionRequest { - /// - [JsonIgnore] - public override string Kind => "planned"; - - /// Protocol version and capabilities the runtime honoured. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + /// Listener UUID returned by host.start. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; - /// The normalised plan. - [JsonPropertyName("plan")] - public required McpInstallPlan Plan { get; set; } + /// Canonical runtime session ID attached to the listener's owning connection. + [JsonPropertyName("sessionId")] + public string SessionId { 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 . +/// Metadata advertised by the AHP host, not a guarantee that history can be resumed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultNegotiationRefused : McpPlanInstallResult +public sealed class HostSessionSummary { - /// - [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; } + /// Optional activity description advertised by the host. + [JsonPropertyName("activity")] + public string? Activity { get; set; } - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("createdAt")] + public string CreatedAt { get; set; } = string.Empty; - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } + /// Last modification time as an ISO 8601 timestamp. + [JsonPropertyName("modifiedAt")] + public string ModifiedAt { get; set; } = string.Empty; - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + /// Stable AHP resource URI identifying the session on this host. + [JsonPropertyName("resource")] + public string Resource { 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; } + /// Unsigned 32-bit AHP session status bitset (0..=4294967295); unknown bits must be preserved. + [JsonPropertyName("status")] + public long Status { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Authoritative title advertised by the host. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; } -/// 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 . +/// The complete live and dormant advertised catalog, without subscribing to sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultHandleRejected : McpPlanInstallResult +public sealed class HostListSessionsResult { - /// - [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; } + /// Complete advertised session summaries, in the host's catalog order. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; 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 . +/// Reads a running host's catalog or the runtime's saved default catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultInvalidRequest : McpPlanInstallResult +internal sealed class HostListSessionsRequest { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; - - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { 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; } +} - /// 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 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; } -/// 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 application-owned session handoff, requested by the supervised hosting participant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultAuthenticationRequired : McpPlanInstallResult +internal sealed class HostCreateSessionRequest { - /// - [JsonIgnore] - public override string Kind => "authentication-required"; + /// Host-selected SDK creation or resume settings, without executable callbacks or tools. + [JsonPropertyName("config")] + public IDictionary Config { get => field ??= new Dictionary(); 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; } + /// Unique identity for this participation, independent of the session lifetime. + [JsonPropertyName("handoffId")] + public string HandoffId { 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; } + /// + /// 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; } + + /// Resume an app-owned durable session instead of creating a new session. + [JsonPropertyName("resume")] + public bool? Resume { get; set; } } -/// Registry or enterprise policy refused the operation. -/// The policy-rejected variant of . +/// Empty acknowledgement for a completed host lifecycle operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultPolicyRejected : McpPlanInstallResult +internal sealed class HostReleaseSessionResult { - /// - [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; } +} - /// Which authority produced the decision. - [JsonPropertyName("source")] - public required McpPlanPolicySource Source { 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 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 . +/// Listener readiness, returned only after binding and the supervised participant's SDK handshake. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultNetworkFailure : McpPlanInstallResult +public sealed class HostStartResult { - /// - [JsonIgnore] - public override string Kind => "network-failure"; + /// GitHub Mission Control environment ID, present when its relay transport is ready. + [JsonPropertyName("environmentId")] + public string? EnvironmentId { 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; } + /// Caller-generated listener UUID. + [JsonPropertyName("hostId")] + public string HostId { 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; } + /// Separate host process ID, when provided by a legacy runtime. Absent for in-process listeners. + [JsonPropertyName("pid")] + public long? Pid { 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; } + /// Secret connection token, absent when authentication is disabled. + [JsonPropertyName("token")] + public string? Token { get; set; } - /// HTTP status code, when the failure was a rejected response. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("statusCode")] - public int? StatusCode { get; set; } + /// Actual bound WebSocket URL, including the allocated port. + [JsonPropertyName("url")] + public string? Url { 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 . +/// GitHub Mission Control registration options. The compute ID is application-owned and stable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultUnsafeRetrieval : McpPlanInstallResult +public sealed class HostGitHubEnvironmentOptions { - /// - [JsonIgnore] - public override string Kind => "unsafe-retrieval"; + /// 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 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; } + /// Human-readable environment display name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Which control refused the retrieval, low cardinality so it can be aggregated without carrying a URL. - [JsonPropertyName("reason")] - public required CatalogUnsafeRetrievalReason 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; } } -/// A card could not be parsed or did not satisfy its declared media type's schema. -/// The malformed-card variant of . +/// Local WebSocket transport options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultMalformedCard : McpPlanInstallResult +public sealed class HostLocalServerOptions { - /// - [JsonIgnore] - public override string Kind => "malformed-card"; + /// Listener hostname. Defaults to 127.0.0.1; explicit non-loopback binds are allowed. + [JsonPropertyName("hostname")] + public string? Hostname { 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; } + /// Listener port. Omitted or zero requests an OS-allocated port. + [JsonPropertyName("port")] + public int? Port { 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; } + /// Require token authentication (default true). Cannot be false with a token. + [JsonPropertyName("requireConnectionToken")] + public bool? RequireConnectionToken { get; set; } - /// How the card failed validation. - [JsonPropertyName("reason")] - public required CatalogMalformedCardReason Reason { get; set; } + /// Nonempty connection token. Generated randomly when required and omitted. + [JsonPropertyName("token")] + public string? Token { 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 . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultContractViolation : McpPlanInstallResult -{ - /// - [JsonIgnore] - public override string Kind => "contract-violation"; - - /// 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; } -} - -/// No transport this runtime can use is available for the requested server. -/// The unavailable-transport variant of . +/// Starts a supervised AHP host with at least one explicitly selected transport. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultUnavailableTransport : McpPlanInstallResult +public sealed class HostStartRequest { - /// - [JsonIgnore] - public override string Kind => "unavailable-transport"; + /// 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; } - /// Why no transport could be offered. - [JsonPropertyName("reason")] - public required CatalogUnavailableTransportReason Reason { get; set; } -} + /// Caller-generated UUID identifying this connection-owned listener. + [JsonPropertyName("hostId")] + public required string HostId { 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 . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultNotInstallable : McpPlanInstallResult -{ - /// - [JsonIgnore] - public override string Kind => "not-installable"; + /// Enables a local WebSocket listener. + [JsonPropertyName("localServer")] + public HostLocalServerOptions? LocalServer { 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; } + /// Ask the owning application to resume its durable AHP sessions. + [JsonPropertyName("resumeFactory")] + public bool? ResumeFactory { get; set; } - /// Why the candidate cannot be installed. - [JsonPropertyName("reason")] - public required CatalogNotInstallableReason Reason { get; set; } + /// Ask the owning SDK application to materialize AHP sessions. + [JsonPropertyName("sessionFactory")] + public bool? SessionFactory { get; set; } } -/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. -/// The unavailable variant of . +/// Empty acknowledgement for a completed host lifecycle operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallResultUnavailable : McpPlanInstallResult +public sealed class HostDisposeResult { - /// - [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; } } -/// The protocol version and capability set a caller requires, supplied on every catalog request so negotiation cannot be skipped by omission. +/// Stops a connection-owned listener and joins its teardown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogClientContract +internal sealed class HostDisposeRequest { - /// 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; } - - /// 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; } + /// Listener UUID. Unknown or successfully stopped IDs are harmless. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; } -/// 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. +/// Private credentials delivered only to a runtime-owned Mission Control hosting participant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpPlanInstallSourceCandidate), "candidate")] -[JsonDerivedType(typeof(McpPlanInstallSourceCard), "card")] -public partial class McpPlanInstallSource +internal sealed class HostGetEnvironmentCredentialsResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - + /// GitHub API base URL for the authenticated service. + [JsonPropertyName("githubApiUrl")] + public string GitHubApiUrl { get; set; } = string.Empty; -/// Plan from a candidate returned by a previous catalog search. -/// The candidate variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallSourceCandidate : McpPlanInstallSource -{ - /// - [JsonIgnore] - public override string Kind => "candidate"; + /// Hostname of the authenticated GitHub service. + [JsonPropertyName("githubHost")] + public string GitHubHost { get; set; } = string.Empty; - /// 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; } + /// Mission Control API origin for environment registration and management. + [JsonPropertyName("missionControlUrl")] + public string MissionControlUrl { get; set; } = string.Empty; - /// 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; } + /// Current bearer token for the authenticated GitHub identity. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// 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. +/// Normalized local WebSocket listener settings. [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 HostLocalServerConfiguration { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - + /// Hostname or IP address to bind. + [JsonPropertyName("hostname")] + public string Hostname { get; set; } = string.Empty; -/// 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 McpServerCardReferenceUrl : McpServerCardReference -{ - /// - [JsonIgnore] - public override string Kind => "url"; + /// Port to bind, with zero requesting OS allocation. + [JsonPropertyName("port")] + public int Port { get; set; } - /// Media type the card is expected to conform to. - [JsonPropertyName("mediaType")] - public required McpServerCardMediaType MediaType { get; set; } + /// Whether the listener requires token authentication. + [JsonPropertyName("requireConnectionToken")] + public bool RequireConnectionToken { 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; } + /// Secret connection token, absent when authentication is disabled. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// An MCP server card supplied inline as an inert document. -/// The embedded variant of . +/// Normalized listener settings delivered only to the supervised hosting participant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpServerCardReferenceEmbedded : McpServerCardReference +internal sealed class HostGetConfigurationResult { - /// - [JsonIgnore] - public override string Kind => "embedded"; + /// Requested GitHub Mission Control registration. + [JsonPropertyName("githubEnvironment")] + public HostGitHubEnvironmentOptions? GitHubEnvironment { 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; } + /// Normalized local listener settings, absent for relay-only hosts. + [JsonPropertyName("localServer")] + public HostLocalServerConfiguration? LocalServer { get; set; } - /// Media type the card is expected to conform to. - [JsonPropertyName("mediaType")] - public required McpServerCardMediaType MediaType { 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; } } -/// Plan from a card supplied directly by the caller, without a preceding search. -/// The card variant of . +/// Empty acknowledgement for a completed host lifecycle operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpPlanInstallSourceCard : McpPlanInstallSource +internal sealed class HostReadyResult { - /// - [JsonIgnore] - public override string Kind => "card"; - - /// The card to plan from: exactly one of a URL or embedded data. - [JsonPropertyName("card")] - public required McpServerCardReference Card { get; set; } } -/// A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. +/// Readiness reported by the supervised hosting participant on its own SDK connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPlanInstallRequest +internal sealed class HostReadyRequest { - /// Protocol version and capabilities the caller requires. - [JsonPropertyName("contract")] - public required CatalogClientContract Contract { 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; } + /// Actual bound WebSocket URL. + [JsonPropertyName("address")] + public string? Address { get; set; } - /// Configuration scope the plan targets. Defaults to user scope when omitted. - [JsonPropertyName("scope")] - public McpPlanScope? Scope { get; set; } + /// Registered environment ID, reported only once the relay transport is connected. + [JsonPropertyName("environmentId")] + public string? EnvironmentId { 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; } + /// Configured secret token, absent when authentication is disabled. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// Management result with contract receipt, or a typed request/negotiation refusal. -/// Polymorphic base type discriminated by kind. +/// One server-discovered hook action from user, repository, plugin, or managed-policy configuration. [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 DiscoveredHook { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// 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; } -/// 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; -} + /// 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; -/// 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 McpInstallationManagementOutcomeRecoveryRequired : McpInstallationManagementOutcome -{ - /// - [JsonIgnore] - public override string Kind => "recovery-required"; -} + /// Configuration tier that contributed this hook action. + [JsonPropertyName("origin")] + public HookOrigin Origin { 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; } + /// 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; } - /// Original connection-owned operation, known before the first confirmation callback. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Human-readable source label, such as a hook file path, settings source, or plugin name. + [JsonPropertyName("source")] + public string? Source { get; set; } } -/// The install-prepared variant of . +/// 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 partial class McpInstallationManagementOutcomeInstallPrepared : McpInstallationManagementOutcome +public sealed class HooksDiscoverResult { - /// - [JsonIgnore] - public override string Kind => "install-prepared"; + /// 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; } - /// Known original operation identity, returned before callback or effects. - [JsonPropertyName("operation")] - public required McpPreparedInstall Operation { 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; } -/// 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; + /// 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; } } - -/// 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. +/// Optional project paths and host-exclusion behavior for server-scoped hook discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogTrustProvenance +public sealed class HooksDiscoverRequest { - /// 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; } + /// 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; } - /// Bounded authority that supplied the trust field. - [JsonPropertyName("source")] - public CatalogTrustSource Source { 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; } + + /// 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; } } -/// A recognised current Agent Finder T1 or T2 trust tier. -/// The current variant of . +/// Active server-driven promotion for a model, including its discount and optional expiry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotCurrent : CatalogTrustSnapshot +public sealed class ModelBillingPromo { - /// - [JsonIgnore] - public override string Status => "current"; + /// Percentage discount (0-100) applied while the promotion is active. May be fractional. + [JsonPropertyName("discountPercent")] + public double? DiscountPercent { 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; } + /// 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; } - /// 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; } + /// Stable identifier for the promotion campaign. + [JsonPropertyName("id")] + public string? Id { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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; } - /// Service-computed T1 or T2 trust tier. - [JsonPropertyName("tier")] - public required CatalogTrustTier Tier { 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; } } -/// Discriminator: the authority omitted trust metadata. -/// The absent variant of . +/// Long context tier pricing (available for models with extended context windows). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotAbsent : CatalogTrustSnapshot +public sealed class ModelBillingTokenPricesLongContext { - /// - [JsonIgnore] - public override string Status => "absent"; + /// 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; } - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } + /// AI Credits cost per billing batch of cached (read) tokens. + [JsonPropertyName("cacheReadPrice")] + public double? CacheReadPrice { 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; } + /// AI Credits cost per billing batch of 1-hour cache-write (cache creation) tokens. + [JsonPropertyName("cacheWrite1hPrice")] + public double? CacheWrite1hPrice { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } -} + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + [JsonPropertyName("cacheWritePrice")] + public double? CacheWritePrice { 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"; + /// 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; } - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } + /// AI Credits cost per billing batch of input tokens. + [JsonPropertyName("inputPrice")] + public double? InputPrice { 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; } + /// 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; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// AI Credits cost per billing batch of output tokens. + [JsonPropertyName("outputPrice")] + public double? OutputPrice { get; set; } } -/// Discriminator: the authority explicitly reported a downgraded assessment. -/// The downgraded variant of . +/// Token-level pricing information for this model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotDowngraded : CatalogTrustSnapshot +public sealed class ModelBillingTokenPrices { - /// - [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; } + /// Number of tokens per standard billing batch. + [JsonPropertyName("batchSize")] + public long? BatchSize { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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; } -/// Discriminator: the authority explicitly revoked the assessment. -/// The revoked variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotRevoked : CatalogTrustSnapshot -{ - /// - [JsonIgnore] - public override string Status => "revoked"; + /// AI Credits cost per billing batch of cached (read) tokens. + [JsonPropertyName("cacheReadPrice")] + public double? CacheReadPrice { 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; } + /// AI Credits cost per billing batch of 1-hour cache-write (cache creation) tokens. + [JsonPropertyName("cacheWrite1hPrice")] + public double? CacheWrite1hPrice { 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; } + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + [JsonPropertyName("cacheWritePrice")] + public double? CacheWritePrice { get; set; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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; } -/// 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"; + /// AI Credits cost per billing batch of input tokens. + [JsonPropertyName("inputPrice")] + public double? InputPrice { 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; } + /// Long context tier pricing (available for models with extended context windows). + [JsonPropertyName("longContext")] + public ModelBillingTokenPricesLongContext? LongContext { 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; } + /// 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; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// AI Credits cost per billing batch of output tokens. + [JsonPropertyName("outputPrice")] + public double? OutputPrice { get; set; } } -/// Discriminator: the trust field was empty, unbounded, or had the wrong JSON type. -/// The malformed variant of . +/// Billing information. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogTrustSnapshotMalformed : CatalogTrustSnapshot +public sealed class ModelBilling { - /// - [JsonIgnore] - public override string Status => "malformed"; + /// 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; } - /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. - [JsonPropertyName("eligibility")] - public required CatalogTrustEligibility Eligibility { get; set; } + /// Billing cost multiplier relative to the base rate. + [JsonPropertyName("multiplier")] + public double? Multiplier { 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; } + /// 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; } - /// Schema version of this runtime-owned snapshot envelope. - [JsonPropertyName("schemaVersion")] - public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } + /// Token-level pricing information for this model. + [JsonPropertyName("tokenPrices")] + public ModelBillingTokenPrices? TokenPrices { get; set; } } -/// Catalogue identity retained from a bound candidate or plan at installation time. +/// Vision-specific limits. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstallationCatalogueIdentity +public sealed class ModelCapabilitiesLimitsVision { - /// Catalogue description retained at install planning time. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Human display name retained from the catalogue candidate. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Maximum image size in bytes. + [JsonPropertyName("max_prompt_image_size")] + public long MaxPromptImageSize { get; set; } - /// Catalogue item URL when supplied by the authority. - [JsonPropertyName("itemUrl")] - public string? ItemUrl { get; set; } + /// Maximum number of images per prompt. + [JsonPropertyName("max_prompt_images")] + public long MaxPromptImages { get; set; } - /// Catalogue publisher retained at install planning time. - [JsonPropertyName("publisher")] - public string? Publisher { get; set; } + /// MIME types the model accepts. + [JsonPropertyName("supported_media_types")] + public IList SupportedMediaTypes { get => field ??= []; set; } +} - /// Authority resource identifier when supplied by the catalogue. - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } +/// Token limits for prompts, outputs, and context window. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModelCapabilitiesLimits +{ + /// Maximum total context window size in tokens. + [JsonPropertyName("max_context_window_tokens")] + public long? MaxContextWindowTokens { get; set; } - /// Catalogue authority/source string that supplied the candidate. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// Maximum number of output/completion tokens. + [JsonPropertyName("max_output_tokens")] + public long? MaxOutputTokens { get; set; } - /// Catalogue trust observation retained at install planning time. - [JsonPropertyName("trustAtInstall")] - public CatalogTrustSnapshot? TrustAtInstall { get; set; } + /// Maximum number of prompt/input tokens. + [JsonPropertyName("max_prompt_tokens")] + public long? MaxPromptTokens { get; set; } - /// Catalogue version retained at install planning time. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Vision-specific limits. + [JsonPropertyName("vision")] + public ModelCapabilitiesLimitsVision? Vision { get; set; } } -/// Durable configuration ownership is distinct from session-specific usability. +/// Feature flags indicating what the model supports. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationSummary +public sealed class ModelCapabilitiesSupports { - /// Catalogue identity retained from the installed plan when available. - [JsonPropertyName("catalogue")] - public InstallationCatalogueIdentity? Catalogue { 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; } - /// Exact alternative retained in the installing receipt. - [JsonPropertyName("choiceId")] - public string ChoiceId { get; set; } = string.Empty; + /// Whether this model supports reasoning effort configuration. + [JsonPropertyName("reasoningEffort")] + public bool? ReasoningEffort { get; set; } - /// Identity retained from the validated original plan. - [JsonPropertyName("identity")] - public McpPlanResourceIdentity Identity { get => field ??= new(); set; } + /// Whether the model supports provider-native thinking. Independent of configurable reasoning effort; omission means unknown. + [JsonPropertyName("thinking")] + public bool? Thinking { get; set; } - /// Exact durable installation receipt identity. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// Whether this model supports canonical tool calling. + [JsonPropertyName("toolCalls")] + public bool? ToolCalls { get; set; } - /// ISO 8601 wall-clock installation time when available. - [JsonPropertyName("installedAt")] - public string? InstalledAt { get; set; } + /// Whether this model supports vision/image input. + [JsonPropertyName("vision")] + public bool? Vision { get; set; } +} - /// Original installing operation, not a fresh management operation. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; +/// Model capabilities and limits. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModelCapabilities +{ + /// Token limits for prompts, outputs, and context window. + [JsonPropertyName("limits")] + public ModelCapabilitiesLimits? Limits { get; set; } - /// Ownership or setup state, never inferred proof of tool usability. - [JsonPropertyName("state")] - public McpInstallationState State { get; set; } + /// Feature flags indicating what the model supports. + [JsonPropertyName("supports")] + public ModelCapabilitiesSupports? Supports { get; set; } } -/// The listed variant of . +/// A service-published message about a model, carrying a stable machine-readable code alongside human-readable text. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeListed : McpInstallationManagementOutcome +public sealed class ModelMessage { - /// - [JsonIgnore] - public override string Kind => "listed"; + /// 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; - /// Owned receipts visible to the selected account and host. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// Human-readable message text intended for display to the user. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// The recovered variant of . +/// Policy state (if applicable). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeRecovered : McpInstallationManagementOutcome +public sealed class ModelPolicy { - /// - [JsonIgnore] - public override string Kind => "recovered"; + /// Current policy state for this model. + [JsonPropertyName("state")] + public ModelPolicyState State { get; set; } - /// Freshly inspected receipts after successful durable reconciliation. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// Usage terms or conditions for this model. + [JsonPropertyName("terms")] + public string? Terms { get; set; } } -/// Exact inert removal plan. No configuration or credentials have changed. +/// Service-published warning text that hosts should display when presenting a model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpUninstallPlan +public sealed class ModelWarningText { - /// Original wall-clock expiry in milliseconds. Applying never renews it. - [JsonPropertyName("expiresAtEpochMs")] - public long ExpiresAtEpochMs { get; set; } + /// 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; } +} - /// Original owned receipt being removed. - [JsonPropertyName("installation")] - public McpInstallationSummary Installation { get => field ??= new(); set; } +/// Copilot model metadata, including identifier, display name, capabilities, policy, billing, reasoning efforts, and picker categories. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class Model +{ + /// Billing information. + [JsonPropertyName("billing")] + public ModelBilling? Billing { get; set; } - /// The original operation, inspectable and cancellable on this same connection. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Model capabilities and limits. + [JsonPropertyName("capabilities")] + public ModelCapabilities Capabilities { get => field ??= new(); set; } - /// Exact configured input slots owned by this installation, never shared OAuth tokens. - [JsonPropertyName("ownedSecretCount")] - public long OwnedSecretCount { get; set; } + /// Default reasoning effort level (only present if model supports reasoning effort). + [JsonPropertyName("defaultReasoningEffort")] + public string? DefaultReasoningEffort { get; set; } - /// One-use original connection and authority-bound plan handle. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Model identifier (e.g., "claude-sonnet-4.5"). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Shared authentication is deliberately retained; revocation is a separate action. - [JsonPropertyName("preservesSharedAuthentication")] - public bool PreservesSharedAuthentication { 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; } - /// Whether removal restores a protected earlier configuration. - [JsonPropertyName("restoresPreviousConfiguration")] - public bool RestoresPreviousConfiguration { 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; } -/// The uninstall-planned variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeUninstallPlanned : McpInstallationManagementOutcome -{ - /// - [JsonIgnore] - public override string Kind => "uninstall-planned"; + /// Model capability category for grouping in the model picker. + [JsonPropertyName("modelPickerCategory")] + public ModelPickerCategory? ModelPickerCategory { get; set; } - /// Original owned removal plan and operation. - [JsonPropertyName("plan")] - public required McpUninstallPlan Plan { get; set; } -} + /// Relative cost tier for token-based billing users. + [JsonPropertyName("modelPickerPriceCategory")] + public ModelPickerPriceCategory? ModelPickerPriceCategory { 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(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 -{ - /// The type discriminator. - [JsonPropertyName("phase")] - public virtual string Phase { get; set; } = string.Empty; -} + /// Display name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + /// Policy state (if applicable). + [JsonPropertyName("policy")] + public ModelPolicy? Policy { get; set; } -/// The preparing variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusPreparing : McpInstallationOperationStatus -{ - /// - [JsonIgnore] - public override string Phase => "preparing"; + /// 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; } - /// Whether cancellation has been requested, not proof that a write was undone. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { 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; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Supported reasoning effort levels (only present if model supports reasoning effort). + [JsonPropertyName("supportedReasoningEfforts")] + public IList? SupportedReasoningEfforts { get; set; } -/// The prepared variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusPrepared : McpInstallationOperationStatus -{ - /// - [JsonIgnore] - public override string Phase => "prepared"; + /// 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; } - /// Whether the inert prepared operation was asked to cancel. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { 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; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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 awaiting-confirmation 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 McpInstallationOperationStatusAwaitingConfirmation : McpInstallationOperationStatus +public sealed class ModelList { - /// - [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; } + /// List of available models with full metadata. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } } -/// The revalidating variant of . +/// RPC data type for ModelsList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusRevalidating : McpInstallationOperationStatus +internal sealed class ModelsListRequest { - /// - [JsonIgnore] - public override string Phase => "revalidating"; - - /// Whether source or authority revalidation was asked to cancel. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { 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; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// The applying variant of . +/// A well-known model in the runtime's built-in catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOperationStatusApplying : McpInstallationOperationStatus +public sealed class BuiltInModelCatalogEntry { - /// - [JsonIgnore] - public override string Phase => "applying"; - - /// Whether applying was asked to cancel; already-started effects retain their lease. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } - - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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; } -/// Terminal mutation result. Uncertainty is not approval, rollback or permission to replay. -/// Polymorphic base type discriminated by kind. +/// 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)] -[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 +public sealed class BuiltInModelCatalog { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Built-in model entries. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } } - -/// The installed variant of . +/// The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeInstalled : McpInstallationOutcome +public sealed class ContentExclusionValidatePoliciesResult { - /// - [JsonIgnore] - public override string Kind => "installed"; - - /// 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; } + /// Validated policy objects in input order. Raw JSON preserves every field returned by the runtime's policy parser. + [JsonPropertyName("policies")] + public IList Policies { get => field ??= []; set; } } -/// The uninstalled variant of . +/// Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeUninstalled : McpInstallationOutcome +internal sealed class ContentExclusionValidatePoliciesRequest { - /// - [JsonIgnore] - public override string Kind => "uninstalled"; + /// Unparsed JSON text expected to hold an array of content-exclusion policies. + [JsonPropertyName("policiesJson")] + public string PoliciesJson { get; set; } = string.Empty; +} - /// Durable removal succeeded but final cleanup remains. - [JsonPropertyName("cleanupPending")] - public required bool CleanupPending { get; set; } +/// 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 Tool +{ + /// Description of what the tool does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Exact removed receipt identity. - [JsonPropertyName("installationId")] - public required string InstallationId { get; set; } + /// Optional instructions for how to use this tool effectively. + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } - /// Original removal operation. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Tool identifier (e.g., "bash", "grep", "str_replace_editor"). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Any grants in the incumbent shared OAuth store remain unowned and retained. - [JsonPropertyName("preservedSharedAuthentication")] - public required bool PreservedSharedAuthentication { get; set; } + /// Optional namespaced name for declarative filtering (e.g., "playwright/navigate" for MCP tools). + [JsonPropertyName("namespacedName")] + public string? NamespacedName { get; set; } - /// Exact owned input slots removed, excluding shared OAuth credentials. - [JsonPropertyName("removedOwnedSecrets")] - public required long RemovedOwnedSecrets { get; set; } + /// JSON Schema for the tool's input parameters. + [JsonPropertyName("parameters")] + public IDictionary? Parameters { get; set; } - /// Whether protected pre-install configuration was restored. - [JsonPropertyName("restoredPreviousConfiguration")] - public required bool RestoredPreviousConfiguration { 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; } } -/// The durable transaction was aborted or fully compensated. -/// The rolled-back variant of . +/// Built-in tools available for the requested model, with their parameters and instructions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeRolledBack : McpInstallationOutcome +public sealed class ToolList { - /// - [JsonIgnore] - public override string Kind => "rolled-back"; - - /// Original connection-owned operation. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } - - /// Cause of the fully aborted or compensated operation. - [JsonPropertyName("reason")] - public required McpInstallationFailureReason Reason { get; set; } + /// List of available built-in tools with metadata. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } } -/// A write may have completed. Recover and inspect durable state before retrying. -/// The recovery-required variant of . +/// Optional model identifier whose tool overrides should be applied to the listing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeRecoveryRequired : McpInstallationOutcome +internal sealed class ToolsListRequest { - /// - [JsonIgnore] - public override string Kind => "recovery-required"; - - /// Operation whose durable result must be recovered and inspected. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Optional model ID — when provided, the returned tool list reflects model-specific overrides. + [JsonPropertyName("model")] + public string? Model { get; set; } } -/// The declined 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 McpInstallationOutcomeDeclined : McpInstallationOutcome +public sealed class AccountQuotaSnapshot { - /// - [JsonIgnore] - public override string Kind => "declined"; + /// Number of requests included in the entitlement, or -1 for unlimited entitlements. + [JsonPropertyName("entitlementRequests")] + public long EntitlementRequests { get; set; } - /// Original operation explicitly declined by the user. - [JsonPropertyName("operationId")] - public required string OperationId { 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; } + + /// Whether additional usage is allowed when quota is exhausted. + [JsonPropertyName("overageAllowedWithExhaustedQuota")] + public bool OverageAllowedWithExhaustedQuota { get; set; } + + /// Percentage of entitlement remaining. + [JsonPropertyName("remainingPercentage")] + public double RemainingPercentage { get; set; } + + /// Date when the quota resets (ISO 8601 string). + [JsonPropertyName("resetDate")] + public DateTimeOffset? ResetDate { get; set; } + + /// Whether usage is still permitted after quota exhaustion. + [JsonPropertyName("usageAllowedWithExhaustedQuota")] + public bool UsageAllowedWithExhaustedQuota { get; set; } + + /// Number of requests used so far this period. + [JsonPropertyName("usedRequests")] + public long UsedRequests { get; set; } } -/// The cancelled variant of . +/// Quota usage snapshots for the resolved user, keyed by quota type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeCancelled : McpInstallationOutcome +public sealed class AccountGetQuotaResult { - /// - [JsonIgnore] - public override string Kind => "cancelled"; - - /// Original operation cancelled before a terminal application result. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Quota snapshots keyed by type (e.g., chat, completions, premium_interactions). + [JsonPropertyName("quotaSnapshots")] + public IDictionary QuotaSnapshots { get => field ??= new Dictionary(); set; } } -/// The refused variant of . +/// RPC data type for AccountGetQuota operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationOutcomeRefused : McpInstallationOutcome +internal sealed class AccountGetQuotaRequest { - /// - [JsonIgnore] - public override string Kind => "refused"; - - /// Present once an operation has been allocated; never a plan handle. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operationId")] - public string? OperationId { 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; } - /// Specific bounded refusal, never a success-shaped fallback. - [JsonPropertyName("reason")] - public required McpInstallationFailureReason Reason { get; set; } + /// Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// The completed variant of . +/// 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)] -public partial class McpInstallationOperationStatusCompleted : McpInstallationOperationStatus +[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 { - /// - [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 McpInstallationOutcome Outcome { get; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// The operation variant of . + +/// Endpoint URLs from the raw Copilot `/copilot_internal/v2/token` user-response passthrough. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeOperation : McpInstallationManagementOutcome +public sealed class CopilotUserResponseEndpoints { - /// - [JsonIgnore] - public override string Kind => "operation"; + /// Copilot API endpoint URL. + [JsonPropertyName("api")] + public string? Api { get; set; } - /// Original-connection operation snapshot. - [JsonPropertyName("operation")] - public required McpInstallationOperationStatus Operation { get; set; } + /// Experimental-service endpoint URL. + [JsonPropertyName("exp")] + public string? Exp { get; set; } + + /// Origin-tracker endpoint URL. + [JsonPropertyName("origin-tracker")] + public string? OriginTracker { get; set; } + + /// Copilot proxy endpoint URL. + [JsonPropertyName("proxy")] + public string? Proxy { get; set; } + + /// Copilot telemetry endpoint URL. + [JsonPropertyName("telemetry")] + public string? Telemetry { get; set; } } -/// The refused variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementOutcomeRefused : McpInstallationManagementOutcome +/// RPC data type for CopilotUserResponseEnterpriseListItem operations. +public sealed class CopilotUserResponseEnterpriseListItem { - /// - [JsonIgnore] - public override string Kind => "refused"; - - /// Specific bounded refusal. - [JsonPropertyName("reason")] - public required McpInstallationFailureReason Reason { get; set; } + /// JavaScript-safe numeric database ID of the enterprise. + [JsonPropertyName("id")] + public long? Id { get; set; } } -/// The outcome variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationManagementResultOutcome : McpInstallationManagementResult +/// RPC data type for CopilotUserResponseOrganizationListItem operations. +public sealed class CopilotUserResponseOrganizationListItem { - /// - [JsonIgnore] - public override string Kind => "outcome"; + /// Numeric database ID of the organization. + [JsonPropertyName("id")] + public double? Id { get; set; } - /// Capabilities actually honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + /// GitHub login of the organization. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Observed management outcome. - [JsonPropertyName("outcome")] - public required McpInstallationManagementOutcome Outcome { get; set; } + /// Display name of the organization. + [JsonPropertyName("name")] + public string? Name { 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 . +/// 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 McpInstallationManagementResultNegotiationRefused : McpInstallationManagementResult +public sealed class CopilotUserResponseQuotaSnapshotsChat { - /// - [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; } + /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. + [JsonPropertyName("entitlement")] + public double? Entitlement { get; set; } - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { 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; } - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } + /// Count of additional pay-per-request usage consumed this period beyond the entitlement. + [JsonPropertyName("overage_count")] + public double? OverageCount { get; set; } - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. + [JsonPropertyName("overage_permitted")] + public bool? OveragePermitted { 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; } + /// Percentage of the entitlement remaining at the snapshot timestamp. + [JsonPropertyName("percent_remaining")] + public double? PercentRemaining { get; set; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } -} + /// Identifier of the quota bucket this snapshot describes. + [JsonPropertyName("quota_id")] + public string? QuotaId { 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 McpInstallationManagementResultInvalidRequest : McpInstallationManagementResult -{ - /// - [JsonIgnore] - public override string Kind => "invalid-request"; + /// Amount of quota remaining at the snapshot timestamp. + [JsonPropertyName("quota_remaining")] + public double? QuotaRemaining { get; set; } - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Unix epoch time, in seconds, when this quota next resets. + [JsonPropertyName("quota_reset_at")] + public double? QuotaResetAt { 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; } -} + /// Remaining entitlement/quota amount at the snapshot timestamp. + [JsonPropertyName("remaining")] + public double? Remaining { get; set; } -/// One Registry string-valued configuration entry for the selected transport. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationInput -{ - /// Exact category declared by the selected choice. - [JsonPropertyName("category")] - public McpPlanValueCategory Category { get; set; } + /// UTC timestamp when this snapshot was captured. + [JsonPropertyName("timestamp_utc")] + public string? TimestampUtc { get; set; } - /// Exact key declared by the selected choice. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// 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; } - /// Explicit non-secret value. Secret placeholders use a separate input channel. - [JsonPropertyName("value")] - public string Value { get; set; } = string.Empty; + /// Whether the entitlement for this category is unlimited. + [JsonPropertyName("unlimited")] + public bool? Unlimited { get; set; } } -/// A request-local value for one exact reviewed placeholder. Never logged or persisted in a plan. +/// 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 sealed class McpInstallationSecret +public sealed class CopilotUserResponseQuotaSnapshotsCompletions { - /// Exact placeholder from the selected choice, not a caller-chosen backend identifier. - [JsonPropertyName("placeholder")] - public string Placeholder { get; set; } = string.Empty; + /// Number of requests/units included in the entitlement for this period; `-1` denotes an unlimited entitlement. + [JsonPropertyName("entitlement")] + public double? Entitlement { get; set; } - /// Fresh explicit secret value. It is omitted from confirmation reviews and telemetry. - [JsonPropertyName("value")] - public string Value { get; set; } = string.Empty; -} + /// 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; } -/// Side-effect-free preparation of one original bound remote MCP choice. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpPrepareInstallRequest -{ - /// Exact selected alternative from that plan. - [JsonPropertyName("choiceId")] - public string ChoiceId { get; set; } = string.Empty; + /// Count of additional pay-per-request usage consumed this period beyond the entitlement. + [JsonPropertyName("overage_count")] + public double? OverageCount { get; set; } - /// Required bound catalogue and confirmed remote installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. + [JsonPropertyName("overage_permitted")] + public bool? OveragePermitted { 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; } + /// Percentage of the entitlement remaining at the snapshot timestamp. + [JsonPropertyName("percent_remaining")] + public double? PercentRemaining { get; set; } - /// Original single-use bound plan, never a client-authored configuration. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Identifier of the quota bucket this snapshot describes. + [JsonPropertyName("quota_id")] + public string? QuotaId { get; set; } - /// An existing local session attached to this connection, not permission to attach one. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Amount of quota remaining at the snapshot timestamp. + [JsonPropertyName("quota_remaining")] + public double? QuotaRemaining { 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; } + /// Unix epoch time, in seconds, when this quota next resets. + [JsonPropertyName("quota_reset_at")] + public double? QuotaResetAt { get; set; } - /// The trusted host presents this choice alongside the exact secret placeholders. - [JsonPropertyName("secretStorage")] - public McpInstallationSecretStorage SecretStorage { get; set; } + /// Remaining entitlement/quota amount at the snapshot timestamp. + [JsonPropertyName("remaining")] + public double? Remaining { get; set; } - /// The exact original source, used transiently only after confirmation. - [JsonPropertyName("source")] - public McpServerCardReference Source { get => field ??= new(); 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; } } -/// An installation result together with the exact honoured contract, or a negotiation refusal. -/// Polymorphic base type discriminated by kind. +/// 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)] -[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 CopilotUserResponseQuotaSnapshotsPremiumInteractions { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// 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; } -/// The outcome variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationResultOutcome : McpInstallationResult -{ - /// - [JsonIgnore] - public override string Kind => "outcome"; + /// Count of additional pay-per-request usage consumed this period beyond the entitlement. + [JsonPropertyName("overage_count")] + public double? OverageCount { get; set; } - /// Capabilities actually honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + /// Whether usage may continue at pay-per-request rates once the entitlement is exhausted. + [JsonPropertyName("overage_permitted")] + public bool? OveragePermitted { get; set; } - /// Terminal result of the original operation. - [JsonPropertyName("outcome")] - public required McpInstallationOutcome Outcome { get; set; } -} + /// Percentage of the entitlement remaining at the snapshot timestamp. + [JsonPropertyName("percent_remaining")] + public double? PercentRemaining { 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 McpInstallationResultNegotiationRefused : McpInstallationResult -{ - /// - [JsonIgnore] - public override string Kind => "negotiation-refused"; + /// Identifier of the quota bucket this snapshot describes. + [JsonPropertyName("quota_id")] + public string? QuotaId { 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; } + /// Amount of quota remaining at the snapshot timestamp. + [JsonPropertyName("quota_remaining")] + public double? QuotaRemaining { get; set; } - /// Lowest caller protocol version this runtime will serve. - [JsonPropertyName("minimumSupportedProtocolVersion")] - public required long MinimumSupportedProtocolVersion { get; set; } + /// Unix epoch time, in seconds, when this quota next resets. + [JsonPropertyName("quota_reset_at")] + public double? QuotaResetAt { get; set; } - /// Whether the version or the capability set was the problem. - [JsonPropertyName("reason")] - public required CatalogNegotiationRefusedReason Reason { get; set; } + /// Remaining entitlement/quota amount at the snapshot timestamp. + [JsonPropertyName("remaining")] + public double? Remaining { get; set; } - /// Protocol version of the runtime that refused the request. - [JsonPropertyName("runtimeProtocolVersion")] - public required long RuntimeProtocolVersion { get; set; } + /// UTC timestamp when this snapshot was captured. + [JsonPropertyName("timestamp_utc")] + public string? TimestampUtc { 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; } + /// 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 subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Whether the entitlement for this category is unlimited. + [JsonPropertyName("unlimited")] + public bool? Unlimited { 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 . +/// 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 McpInstallationResultInvalidRequest : McpInstallationResult +public sealed class CopilotUserResponseQuotaSnapshots { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; + /// 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; } - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { 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; } - /// 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; } + /// 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; } } -/// Applies exactly one previously prepared operation on its original connection. +/// 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)] -internal sealed class McpApplyInstallRequest +public sealed class CopilotUserResponse { - /// Capabilities required by the original prepared operation. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// 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; } - /// Runtime-issued ID already returned by prepareInstall, never reused or rebound. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Opaque analytics tracking identifier for the user, forwarded from the Copilot API. + [JsonPropertyName("analytics_tracking_id")] + public string? AnalyticsTrackingId { get; set; } - /// Same existing attached or privately borrowed session as preparation. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// 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; } } -/// Read-only preparation of one owned removal under fresh selected-session authority. +/// 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)] -internal sealed class McpPlanUninstallRequest +public partial class AuthInfoHmac : AuthInfo { - /// Required authenticated bound installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Type => "hmac"; - /// Exact receipt to inspect, not a server-name guess. - [JsonPropertyName("installationId")] - public string InstallationId { 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; } - /// Existing selected session on the original connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// 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; } } -/// One-use application of the exact retained removal plan. +/// 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)] -internal sealed class McpApplyUninstallRequest +public partial class AuthInfoEnv : AuthInfo { - /// Required authenticated bound installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Type => "env"; - /// Opaque original removal plan, consumed once. - [JsonPropertyName("planHandle")] - public string PlanHandle { 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; } - /// Same existing selected session as removal preparation. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// 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; } } -/// User-configured MCP servers, keyed by server name. +/// 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 sealed class McpConfigList +public partial class AuthInfoToken : AuthInfo { - /// All MCP servers from user config, keyed by name. - [JsonPropertyName("servers")] - public IDictionary Servers { get => field ??= new Dictionary(); set; } + /// + [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; } + + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { 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; } } -/// MCP server name and configuration to add to user configuration. +/// 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)] -internal sealed class McpConfigAddRequest +public partial class AuthInfoTokenProvider : AuthInfo { - /// MCP server configuration (stdio process or remote HTTP/SSE). - [JsonPropertyName("config")] - public JsonElement Config { get; set; } + /// + [JsonIgnore] + public override string Type => "token-provider"; - /// 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)] - [JsonPropertyName("name")] - public string Name { 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; } + + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// Opaque SDK callback registration identifier. + [JsonPropertyName("registrationId")] + public required string RegistrationId { get; set; } } -/// MCP server name and replacement configuration to write to user configuration. +/// 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)] -internal sealed class McpConfigUpdateRequest +public partial class AuthInfoCopilotApiToken : AuthInfo { - /// MCP server configuration (stdio process or remote HTTP/SSE). - [JsonPropertyName("config")] - public JsonElement Config { get; set; } + /// + [JsonIgnore] + public override string Type => "copilot-api-token"; - /// 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)] - [JsonPropertyName("name")] - public string Name { 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 (always the public GitHub host). + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// MCP server name to remove from user configuration. +/// 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)] -internal sealed class McpConfigRemoveRequest +public partial class AuthInfoUser : AuthInfo { - /// OAuth Client ID Metadata Document URL whose persisted credentials should also be removed. - [JsonPropertyName("authClientIdMetadataUrl")] - public string? AuthClientIdMetadataUrl { get; set; } + /// + [JsonIgnore] + public override string Type => "user"; - /// 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)] - [JsonPropertyName("name")] - public string Name { 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; } + + /// OAuth user login. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// MCP server names to enable for new sessions. +/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. +/// The account variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigEnableRequest +public partial class AuthInfoAccount : AuthInfo { - /// 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; } + /// + [JsonIgnore] + public override string Type => "account"; + + /// Host coordinate owned by the account's model provider. + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// Login identifying the provider-owned account. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// MCP server names to disable for new sessions. +/// 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)] -internal sealed class McpConfigDisableRequest +public partial class AuthInfoGhCli : AuthInfo { - /// 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; } + /// + [JsonIgnore] + public override string Type => "gh-cli"; + + /// 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; } + + /// 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; } } -/// Request id naming a cancellable registry search. +/// 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 McpRegistryRequestIdResult +public partial class AuthInfoApiKey : AuthInfo { - /// 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; } + /// + [JsonIgnore] + public override string Type => "api-key"; + + /// The API key. Treat as a secret. + [JsonPropertyName("apiKey")] + public required string ApiKey { 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; } } -/// Servers selected from the registry response. +/// Current authentication state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRegistrySearchResult +public sealed class AccountGetCurrentAuthResult { - /// 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; } + /// Authentication errors from the last auth attempt, if any. + [JsonPropertyName("authErrors")] + public IList? AuthErrors { get; set; } + + /// Current authentication information, if authenticated. + [JsonPropertyName("authInfo")] + public AuthInfo? AuthInfo { get; set; } } -/// Registry search terms, the credential to search under, and the request id that makes the search cancellable. +/// Authenticated account entry returned by `account.getAllUsers`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRegistrySearchRequest +public sealed class AccountAllUsers { - /// 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. + /// Authentication information for this user. [JsonPropertyName("authInfo")] - public JsonElement AuthInfo { 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; } + public AuthInfo AuthInfo { get => field ??= new(); 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; } + /// Opaque identifier accepted by account and model selection APIs. + [JsonPropertyName("selectionId")] + public string? SelectionId { 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; } + /// Associated token, if available. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// Whether the cancel reached a live search. +/// Result of a successful login; throws on failure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRegistryCancelResult +public sealed class AccountLoginResult { - /// 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; } + /// 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; } } -/// Registry search to abandon. +/// Credentials to validate and store. Omit login to resolve the authenticated user from the token. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRegistryCancelRequest +internal sealed class AccountLoginRequest { - /// Request ID from `mcp.registry.allocateRequestId` that the search uses. - [JsonPropertyName("requestId")] - public long RequestId { get; set; } + /// GitHub host URL. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// User login/username. When omitted, the runtime validates the token and resolves the login from GitHub. + [JsonPropertyName("login")] + public string? Login { get; set; } + + /// GitHub authentication token. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// New-work inventory or recovery request under an explicitly selected existing session. +/// Logout result indicating if more users remain. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpInstallationsRequest +public sealed class AccountLogoutResult { - /// 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; + /// Whether other authenticated users remain after logout. + [JsonPropertyName("hasMoreUsers")] + public bool HasMoreUsers { get; set; } } -/// Existing-operation control. A new session selector is deliberately not accepted. +/// User to log out. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpInstallationOperationRequest +internal sealed class AccountLogoutRequest { - /// Original installation wire capability; new-work authentication is not reacquired. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// Authentication information for the user to log out. + [JsonPropertyName("authInfo")] + public AuthInfo? AuthInfo { get; set; } - /// Exact runtime-issued operation ID on the original connection. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Opaque account identifier returned by `account.getAllUsers`. + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// Installed plugin that contributes a discovered extension. +/// The wording the runtime uses for an authentication identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredExtensionPlugin +internal sealed class AccountDescribeAuthInfoResult { - /// Installed plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// One-line description of the identity, suitable after a verb such as "Switched to". + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Sign-in banner announcing the authenticated user. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// Discovered extension metadata and persistent enablement state. +/// Credential-free authentication identity safe to expose to hosts and user interfaces. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredExtension +public sealed class AuthIdentity { - /// Whether this extension's persistent per-ID preference is enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Snapshot of the authenticated user's Copilot subscription info, if known. + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Source-qualified ID accepted by both server and session extension enablement methods. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Name of the environment variable that supplied the credential, when applicable. + [JsonPropertyName("envVar")] + public string? EnvVar { get; set; } - /// Human-readable extension name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Authentication host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Absolute path to the extension entry module, suitable for revealing it in a file manager. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Authenticated login, when available. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Containing plugin metadata for plugin-contributed extensions. - [JsonPropertyName("plugin")] - public DiscoveredExtensionPlugin? Plugin { get; set; } + /// Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. + [JsonPropertyName("registrationId")] + public string? RegistrationId { get; set; } - /// Discovery source. - [JsonPropertyName("source")] - public DiscoveredExtensionSource Source { get; set; } + /// Authentication type. + [JsonPropertyName("type")] + public AuthInfoType Type { get; set; } } -/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. +/// The authentication identity whose shared wording should be rendered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredExtensions +internal sealed class AccountDescribeAuthInfoRequest { - /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state. - [JsonPropertyName("extensions")] - public IList Extensions { get => field ??= []; set; } - - /// Effective extension loading mode. Defaults to load_and_augment when unset. - [JsonPropertyName("mode")] - public DiscoveredExtensionMode Mode { get; set; } + /// Identity to describe. + [JsonPropertyName("identity")] + public AuthIdentity Identity { get => field ??= new(); set; } } -/// Source-qualified extension identifiers to persistently enable for future sessions. +/// Whether the identity can create gists, and why not when it cannot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiscoveredExtensionsEnableRequest +internal sealed class AccountGetGistSupportResult { - /// Source-qualified user or plugin extension IDs to enable. - [JsonPropertyName("ids")] - public IList Ids { get => field ??= []; set; } + /// Machine-readable reason category when unsupported. + [JsonPropertyName("kind")] + public string? Kind { get; set; } + + /// Human-readable explanation to show where the gist action would have been. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// True when the identity can create gists. + [JsonPropertyName("supported")] + public bool Supported { get; set; } } -/// Source-qualified extension identifiers to persistently disable for future sessions. +/// The authentication identity whose gist support should be reported. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiscoveredExtensionsDisableRequest +internal sealed class AccountGetGistSupportRequest { - /// Source-qualified user or plugin extension IDs to disable. - [JsonPropertyName("ids")] - public IList Ids { get => field ??= []; set; } + /// Identity to evaluate. + [JsonPropertyName("identity")] + public AuthIdentity Identity { get => field ??= new(); set; } } -/// Skill installation management result with the honoured contract, or a typed refusal. -/// Polymorphic base type discriminated by kind. +/// An absolute URL on the identity's GitHub host. [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 +internal sealed class AccountGetWebUrlResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Absolute URL for the requested path. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; } - -/// Management outcome for verified Skill inventory, planning and removal. -/// Polymorphic base type discriminated by kind. +/// A path to resolve against the GitHub host an identity is signed in to. [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 +internal sealed class AccountGetWebUrlRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// 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; +} -/// Already-confirmed durable work must be reconciled before new mutations or inventory. -/// The recovery-required variant of . +/// The environment variable supplying a classic personal access token, when one is set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRecoveryRequired : SkillInstallationManagementOutcome +internal sealed class AccountGetTokenEnvVarResult { - /// - [JsonIgnore] - public override string Kind => "recovery-required"; + /// Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the environment. + [JsonPropertyName("name")] + public string? Name { get; set; } } -/// Safe verified Skill review fields. No raw credential, candidate handle or plan handle. -/// Polymorphic base type discriminated by action. +/// Confirmation that the secret values were registered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(SkillInstallationReviewInstall), "install")] -[JsonDerivedType(typeof(SkillInstallationReviewUninstall), "uninstall")] -public partial class SkillInstallationReview +public sealed class SecretsAddFilterValuesResult { - /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; + /// Whether the values were successfully registered. + [JsonPropertyName("ok")] + public bool Ok { get; set; } } - -/// One reviewed Skill file. +/// Secret values to add to the redaction filter. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationFileReview +internal sealed class SecretsAddFilterValuesRequest { - /// 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; } + /// Raw secret values to register for redaction. + [JsonPropertyName("values")] + public IList Values { get => field ??= []; set; } } -/// Source identity retained from Agent Finder and the pinned GitHub descriptor. +/// Concrete configuration file containing an MCP server declaration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationSource +public sealed class McpSourceFile { - /// 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; - - /// 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; + /// RFC 6901 JSON Pointer to the server declaration, when known. + [JsonPropertyName("jsonPointer")] + public string? JsonPointer { get; set; } - /// Root path within the pinned repository. - [JsonPropertyName("root")] - public string Root { get; set; } = string.Empty; + /// Canonical file URI for the configuration document. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; } -/// A user-facing personal Skill installation location without absolute host paths. +/// Plugin identity associated with an MCP server declaration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationLocation +public sealed class McpSourcePlugin { - /// Diagnostics-only absolute host path. Hosts must not display it by default. - [JsonPropertyName("diagnosticsAbsolutePath")] - public string? DiagnosticsAbsolutePath { get; set; } - - /// Safe display label, for example ~/.copilot/skills/run-checks. - [JsonPropertyName("displayLabel")] - public string DisplayLabel { get; set; } = string.Empty; + /// 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; - /// Path relative to the Copilot home. - [JsonPropertyName("relativePath")] - public string RelativePath { get; set; } = string.Empty; + /// 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; } - /// Installation scope. Agent Finder Skills are installed in the user's personal Copilot home. - [JsonPropertyName("scope")] - public SkillInstallationScope Scope { 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; } } -/// Review for installing a verified Skill. -/// The install variant of . +/// 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)] -public partial class SkillInstallationReviewInstall : SkillInstallationReview +public sealed class McpSourceRef { - /// - [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; } - - /// - /// 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; } - - /// Installing never grants immediate use; the Skill is written disabled. - [JsonPropertyName("installsDisabled")] - public required bool InstallsDisabled { 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; - /// Skill invocation name from SKILL.md. - [JsonPropertyName("name")] - public required string Name { get; set; } + /// Configuration file location, when the declaration is file-backed. + [JsonPropertyName("file")] + public McpSourceFile? File { get; set; } - /// Exact verified source identity. - [JsonPropertyName("source")] - public required SkillInstallationSource Source { 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; - /// Exact user-scope target location without an absolute host path. - [JsonPropertyName("target")] - public required SkillInstallationLocation Target { 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; - /// Total reviewed payload size in bytes. - [JsonPropertyName("totalBytes")] - public required long TotalBytes { get; set; } + /// Plugin identity, when the declaration is plugin-provided. + [JsonPropertyName("plugin")] + public McpSourcePlugin? Plugin { get; set; } } -/// Durable verified Skill ownership summary. +/// MCP server discovered by `mcp.discover`, with config source, optional plugin source, transport type, and enabled state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallationSummary +public sealed class DiscoveredMcpServer { - /// Catalogue identity retained at install time. - [JsonPropertyName("catalogue")] - public InstallationCatalogueIdentity Catalogue { get => field ??= new(); set; } - - /// Persisted enablement requested for this installation. - [JsonPropertyName("configuredEnabled")] - public bool ConfiguredEnabled { get; set; } - - /// Exact durable installation receipt identity. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// Canonical identity and location of the effective server declaration. + [JsonPropertyName("effectiveSource")] + public McpSourceRef? EffectiveSource { get; set; } - /// ISO 8601 wall-clock installation time. - [JsonPropertyName("installedAt")] - public string InstalledAt { get; set; } = string.Empty; + /// Whether the server is enabled (not in the disabled list). + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Skill invocation name. + /// 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; - /// Operation that installed this Skill. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; - - /// Ownership state observed from files and receipts. - [JsonPropertyName("ownershipState")] - public SkillInstallationOwnershipState OwnershipState { get; set; } - - /// Bound-session load observation. - [JsonPropertyName("sessionState")] - public SkillInstallationSessionState SessionState { get; set; } - - /// Exact retained verified source identity. + /// Configuration source: user, workspace, plugin, or builtin. [JsonPropertyName("source")] - public SkillInstallationSource Source { get => field ??= new(); set; } - - /// User-facing installation location without an absolute host path. - [JsonPropertyName("target")] - public SkillInstallationLocation Target { get => field ??= new(); set; } -} - -/// Review for uninstalling an owned verified Skill. -/// The uninstall variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationReviewUninstall : SkillInstallationReview -{ - /// - [JsonIgnore] - public override string Action => "uninstall"; - - /// Catalogue identity retained at install time. - [JsonPropertyName("catalogue")] - public required InstallationCatalogueIdentity Catalogue { get; set; } - - /// Files recorded by the installation receipt. - [JsonPropertyName("files")] - public required IList Files { get; set; } + public McpServerSource Source { get; set; } - /// Whether current files differ from the receipt. Apply refuses drift. - [JsonPropertyName("filesModified")] - public required bool FilesModified { get; set; } + /// Plugin name that provided this server, when source is plugin. + [JsonPropertyName("sourcePlugin")] + public string? SourcePlugin { get; set; } - /// Owned installation being removed. - [JsonPropertyName("installation")] - public required SkillInstallationSummary Installation { get; set; } + /// Plugin version that provided this server, when source is plugin. + [JsonPropertyName("sourcePluginVersion")] + public string? SourcePluginVersion { get; set; } - /// Total receipt-owned payload size in bytes. - [JsonPropertyName("totalBytes")] - public required long TotalBytes { get; set; } + /// Server transport type: stdio, http, sse (deprecated), or memory. + [JsonPropertyName("type")] + public DiscoveredMcpServerType? Type { get; set; } } -/// A computed Skill install plan. Nothing has been applied. +/// MCP servers discovered from user, workspace, plugin, and built-in sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillInstallPlan +public sealed class McpDiscoverResult { - /// Original wall-clock expiry as an ISO 8601 timestamp. - [JsonPropertyName("expiresAt")] - public string ExpiresAt { get; set; } = string.Empty; - - /// Original operation identifier returned before confirmation. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; - - /// One-use plan handle, bound to the original candidate authority. - [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; } + /// MCP servers discovered from all sources. + [JsonPropertyName("servers")] + public IList Servers { get => field ??= []; set; } } -/// A verified Skill installation plan was prepared. -/// The install-planned variant of . +/// Optional working directory used as context for MCP server discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeInstallPlanned : SkillInstallationManagementOutcome +internal sealed class McpDiscoverRequest { - /// - [JsonIgnore] - public override string Kind => "install-planned"; + /// 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; } - /// Prepared install plan. - [JsonPropertyName("plan")] - public required SkillInstallPlan Plan { get; set; } + /// Working directory used as context for discovery (e.g., plugin resolution). + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Owned Skill installations were listed. -/// The listed variant of . +/// Names of the MCP servers the runtime supplies itself. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeListed : SkillInstallationManagementOutcome +internal sealed class McpListBuiltinServerNamesResult { - /// - [JsonIgnore] - public override string Kind => "listed"; - - /// Owned Skill installation summaries. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// Every built-in server name. A host disabling the built-ins wholesale suppresses exactly this set. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } } -/// Recovery completed and inventory was inspected. -/// The recovered variant of . +/// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRecovered : SkillInstallationManagementOutcome +internal sealed class McpShouldExcludeGitHubToolsResult { - /// - [JsonIgnore] - public override string Kind => "recovered"; - - /// Owned Skill installation summaries after recovery. - [JsonPropertyName("installations")] - public required IList Installations { get; set; } + /// 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; } } -/// Interrupted work was safely compensated and the pending marker was cleared. -/// The rolled-back variant of . +/// The session tool filters that decide whether the session still has a shell to run `gh` with. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRolledBack : SkillInstallationManagementOutcome +internal sealed class McpShouldExcludeGitHubToolsRequest { - /// - [JsonIgnore] - public override string Kind => "rolled-back"; + /// The session's tool allowlist, when it set one. Omitted means the session constrains nothing this way. + [JsonPropertyName("availableTools")] + public IList? AvailableTools { get; set; } - /// Original operation identity. - [JsonPropertyName("operation_id")] - public required string OperationId { get; set; } + /// The session's tool denylist, when it set one. Omitted means the session constrains nothing this way. + [JsonPropertyName("excludedTools")] + public IList? ExcludedTools { get; set; } - /// Cause of the compensation. - [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + /// 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; } } -/// A computed Skill uninstall plan. Nothing has been removed. +/// 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)] -public sealed class SkillUninstallPlan +[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 { - /// 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; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; +/// 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; } - /// Safe review to present before applying removal. - [JsonPropertyName("review")] - public SkillInstallationReview Review { get => field ??= new(); set; } + /// Protocol version of the runtime that served the request. + [JsonPropertyName("runtimeProtocolVersion")] + public long RuntimeProtocolVersion { get; set; } } -/// An owned Skill uninstall plan was prepared. -/// The uninstall-planned variant of . +/// 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 partial class SkillInstallationManagementOutcomeUninstallPlanned : SkillInstallationManagementOutcome +public sealed class McpPlanConfigurationChange { - /// - [JsonIgnore] - public override string Kind => "uninstall-planned"; + /// Names of the configuration fields the change would set, without their values. + [JsonPropertyName("changedFields")] + public IList ChangedFields { get => field ??= []; set; } - /// Prepared uninstall plan. - [JsonPropertyName("plan")] - public required SkillUninstallPlan Plan { 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; -/// 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; -} + /// 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; } -/// Original operation progress discriminator. -/// The preparing variant of . + /// 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; } +} + +/// Normalised identity of the MCP server a plan targets, independent of how the card spelled it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusPreparing : SkillInstallationOperationStatus +public sealed class McpPlanResourceIdentity { - /// - [JsonIgnore] - public override string Phase => "preparing"; + /// 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; - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Registry identifier of the server, when it came from a registry. + [JsonPropertyName("registryId")] + public string? RegistryId { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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; } } -/// Original operation progress discriminator. -/// The prepared variant of . +/// Outcome of evaluating the planned server against registry and enterprise policy. Evaluation is read-only. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusPrepared : SkillInstallationOperationStatus +public sealed class McpPlanPolicyResult { - /// - [JsonIgnore] - public override string Phase => "prepared"; + /// What policy decided for this server. + [JsonPropertyName("decision")] + public McpPlanPolicyDecision Decision { get; set; } - /// 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("reason")] + public string? Reason { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Which authority produced the decision. + [JsonPropertyName("source")] + public McpPlanPolicySource Source { get; set; } } -/// Original operation progress discriminator. -/// The awaiting-confirmation variant of . +/// 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)] -public partial class SkillInstallationOperationStatusAwaitingConfirmation : SkillInstallationOperationStatus +public sealed class CardDigest { - /// - [JsonIgnore] - public override string Phase => "awaiting-confirmation"; - - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Digest algorithm and canonical representation. + [JsonPropertyName("algorithm")] + public CardDigestAlgorithm Algorithm { get; set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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; } -/// Original operation progress discriminator. -/// The revalidating variant of . +/// 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)] -public partial class SkillInstallationOperationStatusRevalidating : SkillInstallationOperationStatus +public sealed class McpPlanProvenance { - /// - [JsonIgnore] - public override string Phase => "revalidating"; + /// 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; - /// Whether cancellation has been requested. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Semantic digest of the exact validated JSON content bound to the plan handle. + [JsonPropertyName("cardDigest")] + public CardDigest CardDigest { get => field ??= new(); set; } - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// JSON MCP media type the validated card was interpreted as. + [JsonPropertyName("mediaType")] + public McpServerCardMediaType MediaType { 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; } -/// Original operation progress discriminator. -/// The applying variant of . +/// Where a plan would be written. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOperationStatusApplying : SkillInstallationOperationStatus +public sealed class McpPlanTarget { - /// - [JsonIgnore] - public override string Phase => "applying"; + /// 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; - /// Whether cancellation has been requested; already-started effects require recovery. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; set; } + /// Configuration scope the plan targets. + [JsonPropertyName("scope")] + public McpPlanScope Scope { get; set; } +} - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "installMethod", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpPlanTransportChoicePackage), "package")] +[JsonDerivedType(typeof(McpPlanTransportChoiceRemote), "remote")] +public partial class McpPlanTransportChoice +{ + /// The type discriminator. + [JsonPropertyName("installMethod")] + public virtual string InstallMethod { get; set; } = string.Empty; } -/// Terminal verified Skill mutation result. + +/// 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)] [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 +[JsonDerivedType(typeof(McpPlanRequiredValueScalar), "scalar")] +[JsonDerivedType(typeof(McpPlanRequiredValueEnum), "enum")] +public partial class McpPlanRequiredValue { /// The type discriminator. [JsonPropertyName("kind")] @@ -4829,313 +4688,285 @@ public partial class SkillInstallationOutcome } -/// The verified Skill was installed disabled. -/// The installed variant of . +/// 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 partial class SkillInstallationOutcomeInstalled : SkillInstallationOutcome +public partial class McpPlanRequiredValueScalar : McpPlanRequiredValue { /// [JsonIgnore] - public override string Kind => "installed"; + public override string Kind => "scalar"; - /// Durable installed Skill summary. - [JsonPropertyName("installation")] - public required SkillInstallationSummary Installation { get; set; } -} + /// Where the value is applied when the server is launched. + [JsonPropertyName("category")] + public required McpPlanValueCategory Category { 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"; + /// 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; } - /// Removed installation identity. - [JsonPropertyName("installationId")] - public required string InstallationId { 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; } - /// Original removal operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Whether the value may be supplied more than once. + [JsonPropertyName("isRepeated")] + public required bool IsRepeated { get; set; } -/// The durable transaction was aborted or fully compensated. -/// The rolled-back variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeRolledBack : SkillInstallationOutcome -{ - /// - [JsonIgnore] - public override string Kind => "rolled-back"; + /// 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; } - /// Original operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } + /// Whether the value must be present for the plan to be applicable. + [JsonPropertyName("required")] + public required bool Required { get; set; } - /// Cause of the fully aborted or compensated operation. - [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { 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; } + + /// Scalar type the value must conform to. + [JsonPropertyName("valueType")] + public required McpPlanScalarValueType ValueType { get; set; } } -/// A write may have completed. Recover and inspect durable state before retrying. -/// The recovery-required 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 SkillInstallationOutcomeRecoveryRequired : SkillInstallationOutcome +public partial class McpPlanRequiredValueEnum : McpPlanRequiredValue { /// [JsonIgnore] - public override string Kind => "recovery-required"; + public override string Kind => "enum"; - /// Operation whose durable result must be recovered and inspected. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Where the value is applied when the server is launched. + [JsonPropertyName("category")] + public required McpPlanValueCategory Category { get; set; } -/// The user declined the confirmation request. -/// The declined variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeDeclined : SkillInstallationOutcome -{ - /// - [JsonIgnore] - public override string Kind => "declined"; + /// 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; } - /// Original operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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; } -/// The operation was cancelled before a terminal mutation. -/// The cancelled variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeCancelled : SkillInstallationOutcome -{ - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// Non-empty permitted value set. Inert untrusted data. + [JsonPropertyName("enumValues")] + public required IList EnumValues { get; set; } - /// Original operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { get; set; } -} + /// Whether the value may be supplied more than once. + [JsonPropertyName("isRepeated")] + public required bool IsRepeated { get; set; } -/// The operation was refused without applying changes. -/// The refused variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationOutcomeRefused : SkillInstallationOutcome -{ - /// - [JsonIgnore] - public override string Kind => "refused"; + /// 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; } - /// Present once an operation has been allocated. + /// Whether the value must be present for the plan to be applicable. + [JsonPropertyName("required")] + public required bool Required { 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("operationId")] - public string? OperationId { get; set; } + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Bounded refusal reason. - [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + /// Discriminator: the value must be one of `enumValues`. + [JsonPropertyName("valueType")] + public required McpPlanEnumValueType ValueType { get; set; } } -/// Original operation progress discriminator. -/// The completed variant of . +/// 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 partial class SkillInstallationOperationStatusCompleted : SkillInstallationOperationStatus +public sealed class McpPlanSecretPlaceholder { - /// - [JsonIgnore] - public override string Phase => "completed"; - - /// Whether cancellation was requested before the terminal result. - [JsonPropertyName("cancellationRequested")] - public required bool CancellationRequested { get; 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; - /// Original runtime-issued operation identity. - [JsonPropertyName("operationId")] - public required string OperationId { 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; - /// Immutable terminal receipt. - [JsonPropertyName("outcome")] - public required SkillInstallationOutcome Outcome { 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; } } -/// Original-connection operation snapshot. -/// The operation variant of . +/// 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 SkillInstallationManagementOutcomeOperation : SkillInstallationManagementOutcome +public partial class McpPlanTransportChoicePackage : McpPlanTransportChoice { /// [JsonIgnore] - public override string Kind => "operation"; + public override string InstallMethod => "package"; - /// Operation status. - [JsonPropertyName("operation")] - public required SkillInstallationOperationStatus Operation { 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; } -/// 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"; + /// 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; } - /// Safe reload or reconciliation diagnostics. - [JsonPropertyName("diagnostics")] - public required IList Diagnostics { 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; } - /// Updated installation summary. - [JsonPropertyName("installation")] - public required SkillInstallationSummary Installation { get; set; } -} + /// Typed values this choice requires, excluding secrets. + [JsonPropertyName("requiredValues")] + public required IList RequiredValues { get; set; } -/// The management request was refused. -/// The refused variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SkillInstallationManagementOutcomeRefused : SkillInstallationManagementOutcome -{ - /// - [JsonIgnore] - public override string Kind => "refused"; + /// Secrets this choice requires, referenced by placeholder only. + [JsonPropertyName("secretPlaceholders")] + public required IList SecretPlaceholders { get; set; } - /// Bounded refusal reason. - [JsonPropertyName("reason")] - public required SkillInstallationFailureReason Reason { get; set; } + /// Local process transport this package choice would use. + [JsonPropertyName("transport")] + public required McpPlanPackageTransport Transport { get; set; } } -/// The outcome 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 SkillInstallationManagementResultOutcome : SkillInstallationManagementResult +public partial class McpPlanTransportChoiceRemote : McpPlanTransportChoice { /// [JsonIgnore] - public override string Kind => "outcome"; + public override string InstallMethod => "remote"; - /// Capabilities honoured for this request. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { 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; } - /// Observed management outcome. - [JsonPropertyName("outcome")] - public required SkillInstallationManagementOutcome Outcome { 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; } + + /// 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; } + + /// Endpoint transport this remote choice would use. + [JsonPropertyName("transport")] + public required McpPlanRemoteTransport Transport { 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 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 partial class SkillInstallationManagementResultNegotiationRefused : SkillInstallationManagementResult +public sealed class McpInstallPlan { - /// - [JsonIgnore] - public override string Kind => "negotiation-refused"; + /// 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; } - /// 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; } + /// Normalised identity of the server the plan would install. + [JsonPropertyName("identity")] + public McpPlanResourceIdentity Identity { get => field ??= new(); 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; } + /// 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; - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; 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; -/// 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"; + /// Outcome of evaluating the server against registry and enterprise policy. + [JsonPropertyName("policy")] + public McpPlanPolicyResult Policy { get => field ??= new(); set; } - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { 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; } - /// 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; } -} + /// 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; } -/// 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; + /// Whether applying this plan would require an MCP reload to take effect. Planning itself never reloads. + [JsonPropertyName("reloadRequired")] + public bool ReloadRequired { get; set; } - /// Required authenticated bound catalogue and Skill installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); 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; } - /// Existing local session attached to this connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; -} + /// Configuration scope and key the plan would write to. + [JsonPropertyName("target")] + public McpPlanTarget Target { get => field ??= new(); 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; + /// 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; } } - -/// The outcome 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 SkillInstallationResultOutcome : SkillInstallationResult +public partial class McpPlanInstallResultPlanned : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "outcome"; + public override string Kind => "planned"; - /// Capabilities honoured for this request. + /// Protocol version and capabilities the runtime honoured. [JsonPropertyName("negotiated")] public required CatalogNegotiatedContract Negotiated { get; set; } - /// Terminal operation outcome. - [JsonPropertyName("outcome")] - public required SkillInstallationOutcome Outcome { get; set; } + /// The normalised plan. + [JsonPropertyName("plan")] + public required McpInstallPlan Plan { 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 SkillInstallationResultNegotiationRefused : SkillInstallationResult +public partial class McpPlanInstallResultNegotiationRefused : McpPlanInstallResult { /// [JsonIgnore] @@ -5168,286 +4999,267 @@ public partial class SkillInstallationResultNegotiationRefused : SkillInstallati public required IList UnsupportedCapabilities { 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 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 partial class SkillInstallationResultInvalidRequest : SkillInstallationResult +public partial class McpPlanInstallResultHandleRejected : McpPlanInstallResult { /// [JsonIgnore] - public override string Kind => "invalid-request"; + public override string Kind => "handle-rejected"; - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Which kind of handle was presented. + [JsonPropertyName("handleType")] + public required CatalogHandleType HandleType { get; set; } - /// Human-readable explanation, safe to surface. Never echoes the offending value, nor a query, URL, handle, or secret. + /// 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; } } -/// Applies exactly one retained verified Skill installation plan. +/// 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 SkillApplyInstallRequest +public partial class McpPlanInstallResultInvalidRequest : McpPlanInstallResult { - /// Required authenticated bound catalogue and Skill installation capabilities. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// Opaque original plan, consumed once. - [JsonPropertyName("planHandle")] - public string PlanHandle { get; set; } = string.Empty; + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } - /// Same existing selected session as planning. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { 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; } } -/// Read-only preparation of one owned Skill removal under fresh selected-session authority. +/// 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)] -internal sealed class SkillPlanUninstallRequest +public partial class McpPlanInstallResultAuthenticationRequired : McpPlanInstallResult { - /// Required authenticated bound installation contract. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "authentication-required"; - /// Exact receipt to inspect. - [JsonPropertyName("installationId")] - public string InstallationId { 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; } - /// Existing selected local session on this connection. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { 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; } } -/// One-use application of the exact retained Skill removal plan. +/// Registry or enterprise policy refused the operation. +/// The policy-rejected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillApplyUninstallRequest +public partial class McpPlanInstallResultPolicyRejected : McpPlanInstallResult { - /// Required authenticated bound installation contract. - [JsonPropertyName("contract")] - public CatalogClientContract Contract { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "policy-rejected"; - /// Opaque original removal plan, consumed once. - [JsonPropertyName("planHandle")] - public string PlanHandle { 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; } - /// Same existing selected session as removal preparation. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { get; set; } = string.Empty; + /// Which authority produced the decision. + [JsonPropertyName("source")] + public required McpPlanPolicySource Source { get; set; } } -/// Server-side skill metadata, including name, description, source, enabled/invocable state, path, project path, and argument hint. +/// 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 sealed class ServerSkill +public partial class McpPlanInstallResultNetworkFailure : McpPlanInstallResult { - /// 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 (based on global config). - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "network-failure"; - /// Absolute path to the skill file. - [JsonPropertyName("path")] - public string? Path { 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 project path this skill belongs to (only for project/inherited skills). - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } + /// Categorised failure, low cardinality so it can be aggregated without carrying a URL. + [JsonPropertyName("reason")] + public required CatalogNetworkFailureReason Reason { get; set; } - /// Source location type (e.g., project, personal-copilot, plugin, builtin). - [JsonPropertyName("source")] - public SkillSource Source { 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; } - /// Whether the skill can be invoked by the user as a slash command. - [JsonPropertyName("userInvocable")] - public bool UserInvocable { get; set; } + /// HTTP status code, when the failure was a rejected response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("statusCode")] + public int? StatusCode { get; set; } } -/// Skills discovered across global and project sources. +/// 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 ServerSkillList +public partial class McpPlanInstallResultUnsafeRetrieval : McpPlanInstallResult { - /// 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 Kind => "unsafe-retrieval"; - /// All discovered skills across all sources. - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; 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; } + + /// Which control refused the retrieval, low cardinality so it can be aggregated without carrying a URL. + [JsonPropertyName("reason")] + public required CatalogUnsafeRetrievalReason Reason { get; set; } } -/// Optional project paths and additional skill directories to include in discovery. +/// 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)] -internal sealed class SkillsDiscoverRequest +public partial class McpPlanInstallResultMalformedCard : McpPlanInstallResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "malformed-card"; - /// Optional skill scan paths to exclude from discovery. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { 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; } - /// Optional list of project directory paths to scan for project-scoped skills. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { 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; } - /// Optional list of additional skill directory paths to include. - [JsonPropertyName("skillDirectories")] - public IList? SkillDirectories { get; set; } + /// How the card failed validation. + [JsonPropertyName("reason")] + public required CatalogMalformedCardReason Reason { get; set; } } -/// Canonical directory where skills can be discovered or created, with scope, preference, and optional project path. +/// 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 sealed class SkillDiscoveryPath +public partial class McpPlanInstallResultContractViolation : McpPlanInstallResult { - /// Absolute path of the create/discovery target (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 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; } - - /// The input project path this directory was derived from (only for project scope). - [JsonPropertyName("projectPath")] - public string? ProjectPath { get; set; } + /// + [JsonIgnore] + public override string Kind => "contract-violation"; - /// Which tier this directory belongs to. - [JsonPropertyName("scope")] - public SkillDiscoveryScope Scope { 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; } -/// Canonical locations where skills can be created so the runtime will recognize them. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillDiscoveryPathList -{ - /// Canonical skill create/discovery directories, in priority order. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// Which rule the response broke. + [JsonPropertyName("reason")] + public required CatalogContractViolationReason Reason { get; set; } } -/// Optional project paths to enumerate. +/// No transport this runtime can use is available for the requested server. +/// The unavailable-transport variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsGetDiscoveryPathsRequest +public partial class McpPlanInstallResultUnavailableTransport : McpPlanInstallResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "unavailable-transport"; - /// Optional skill scan paths to exclude from discovery. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { 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; } - /// Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. - [JsonPropertyName("projectPaths")] - public IList? ProjectPaths { get; set; } + /// Why no transport could be offered. + [JsonPropertyName("reason")] + public required CatalogUnavailableTransportReason Reason { get; set; } } -/// Inventory request under an explicitly selected existing session. +/// 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)] -internal sealed class SkillInstallationsRequest +public partial class McpPlanInstallResultNotInstallable : McpPlanInstallResult { - /// 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; -} + /// + [JsonIgnore] + public override string Kind => "not-installable"; -/// Existing-operation control. A new session selector is deliberately not accepted. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillInstallationOperationRequest -{ - /// Required authenticated bound Skill installation capability. - [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; } - /// Exact runtime-issued operation ID on the original connection. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// Why the candidate cannot be installed. + [JsonPropertyName("reason")] + public required CatalogNotInstallableReason Reason { get; set; } } -/// Persisted enablement update for one owned Skill installation. +/// 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)] -internal sealed class SkillSetEnabledRequest +public partial class McpPlanInstallResultUnavailable : McpPlanInstallResult { - /// 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 => "unavailable"; - /// Existing selected local session to reconcile after persistence. - [JsonPropertyName("policySessionId")] - public string PolicySessionId { 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; } -/// Skill names to mark as disabled in global configuration, replacing any previous list. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsConfigSetDisabledSkillsRequest -{ - /// List of skill names to disable. - [JsonPropertyName("disabledSkills")] - public IList DisabledSkills { get => field ??= []; set; } + /// Why the operation is unavailable. + [JsonPropertyName("reason")] + public required CatalogUnavailableReason Reason { get; set; } } -/// Adds or removes a single skill from the global disabled list, leaving every other entry untouched. +/// 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)] -internal sealed class SkillsConfigSetSkillDisabledRequest +public sealed class CatalogClientContract { - /// True to disable the skill, false to enable it. - [JsonPropertyName("disabled")] - public bool Disabled { get; set; } + /// 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; } - /// Name of the skill to add to or remove from the disabled list. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } } -/// Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success. +/// 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)] [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 +[JsonDerivedType(typeof(McpPlanInstallSourceCandidate), "candidate")] +[JsonDerivedType(typeof(McpPlanInstallSourceCard), "card")] +public partial class McpPlanInstallSource { /// The type discriminator. [JsonPropertyName("kind")] @@ -5455,51 +5267,39 @@ public partial class CatalogSearchResult } -/// 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. +/// Plan from a candidate returned by a previous catalog search. +/// The candidate 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 McpPlanInstallSourceCandidate : McpPlanInstallSource { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - + /// + [JsonIgnore] + public override string Kind => "candidate"; -/// 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)] -public sealed class CatalogMcpServerCandidateProvenance -{ - /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. + /// 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)] - [JsonPropertyName("authority")] - public string Authority { get; set; } = string.Empty; - - /// JSON MCP media type advertised for the referenced card. - [JsonPropertyName("mediaType")] - public McpServerCardMediaType MediaType { get; set; } + [MaxLength(256)] + [JsonPropertyName("candidateHandle")] + public required string CandidateHandle { 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; + /// 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; } } -/// 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. +/// 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)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(CatalogCandidateSourceUrl), "url")] -[JsonDerivedType(typeof(CatalogCandidateSourceEmbedded), "embedded")] -public partial class CatalogCandidateSource +[JsonDerivedType(typeof(McpServerCardReferenceUrl), "url")] +[JsonDerivedType(typeof(McpServerCardReferenceEmbedded), "embedded")] +public partial class McpServerCardReference { /// The type discriminator. [JsonPropertyName("kind")] @@ -5507,655 +5307,616 @@ public partial class CatalogCandidateSource } -/// Candidate whose card is retrieved from a URL through the runtime's hardened fetch boundary. -/// The url 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 CatalogCandidateSourceUrl : CatalogCandidateSource +public partial class McpServerCardReferenceUrl : McpServerCardReference { /// [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. + /// Media type the card is expected to conform to. + [JsonPropertyName("mediaType")] + public required McpServerCardMediaType MediaType { 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; } } -/// Candidate whose card reference arrived inline. The document and its content-derived properties stay behind the runtime boundary. -/// The embedded 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 CatalogCandidateSourceEmbedded : CatalogCandidateSource +public partial class McpServerCardReferenceEmbedded : McpServerCardReference { /// [JsonIgnore] public override string Kind => "embedded"; + + /// 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; } } -/// 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 . +/// 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 CatalogCandidateMcpServer : CatalogCandidate +public partial class McpPlanInstallSourceCard : McpPlanInstallSource { /// [JsonIgnore] - public override string Kind => "mcp-server"; + public override string Kind => "card"; - /// 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; } + /// The card to plan from: exactly one of a URL or embedded data. + [JsonPropertyName("card")] + public required McpServerCardReference Card { 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; } +/// 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 sealed class McpPlanInstallRequest +{ + /// Protocol version and capabilities the caller requires. + [JsonPropertyName("contract")] + public required CatalogClientContract Contract { 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. + /// 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)] - [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 MCP server can be planned for installation, and if policy prevents it. - [JsonPropertyName("installability")] - public required CatalogMcpServerInstallability Installability { get; set; } - - /// JSON MCP media type of the underlying card. - [JsonPropertyName("mediaType")] - public required McpServerCardMediaType MediaType { get; set; } - - /// Where the catalog reference was observed, without the card itself or any content digest. - [JsonPropertyName("provenance")] - public required CatalogMcpServerCandidateProvenance Provenance { get; set; } + [MaxLength(256)] + [JsonPropertyName("policySessionId")] + public string? PolicySessionId { 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; } + /// Configuration scope the plan targets. Defaults to user scope when omitted. + [JsonPropertyName("scope")] + public McpPlanScope? Scope { 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. + /// What to plan: either a candidate handle from a previous search, or a card supplied directly. [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; } + public required McpPlanInstallSource Source { 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. +/// Management result with contract receipt, or a typed request/negotiation refusal. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogAiSkillCandidateProvenance +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpInstallationManagementResultOutcome), "outcome")] +[JsonDerivedType(typeof(McpInstallationManagementResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(McpInstallationManagementResultInvalidRequest), "invalid-request")] +public partial class McpInstallationManagementResult { - /// 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; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Media type advertised for the referenced AI skill card. - [JsonPropertyName("mediaType")] - public CatalogAiSkillMediaType 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; +/// 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; } -/// 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 . + +/// 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 CatalogCandidateAiSkill : CatalogCandidate +public partial class McpInstallationManagementOutcomeRecoveryRequired : McpInstallationManagementOutcome { /// [JsonIgnore] - public override string Kind => "ai-skill"; - - /// 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; } + public override string Kind => "recovery-required"; +} - /// 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; } +/// 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; } - /// 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, known before the first confirmation callback. + [JsonPropertyName("operationId")] + public string OperationId { 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; } +/// The install-prepared variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationManagementOutcomeInstallPrepared : McpInstallationManagementOutcome +{ + /// + [JsonIgnore] + public override string Kind => "install-prepared"; - /// Whether this AI skill candidate can be planned for verified installation in the selected session. - [JsonPropertyName("installability")] - public required CatalogAiSkillInstallability Installability { get; set; } + /// Known original operation identity, returned before callback or effects. + [JsonPropertyName("operation")] + public required McpPreparedInstall Operation { get; set; } +} - /// Media type of the underlying AI skill card. - [JsonPropertyName("mediaType")] - public required CatalogAiSkillMediaType MediaType { 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; +} - /// 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. +/// 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")] - [MaxLength(200)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("publisher")] - public string? Publisher { get; set; } + [MinLength(20)] + [MaxLength(64)] + [JsonPropertyName("observedAt")] + public DateTimeOffset ObservedAt { 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. + /// Bounded authority that supplied the trust field. [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; } + public CatalogTrustSource Source { 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. +/// A recognised current Agent Finder T1 or T2 trust tier. +/// The current variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogAgentPluginCandidateProvenance +public partial class CatalogTrustSnapshotCurrent : CatalogTrustSnapshot { - /// 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 Status => "current"; - /// Canonical Agent Plugin media type. - [JsonPropertyName("mediaType")] - public CatalogAgentPluginMediaType MediaType { 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; } - /// 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; -} + /// 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; } -/// 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; + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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; + /// Service-computed T1 or T2 trust tier. + [JsonPropertyName("tier")] + public required CatalogTrustTier Tier { 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 . +/// Discriminator: the authority omitted trust metadata. +/// The absent variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogCandidatePlugin : CatalogCandidate +public partial class CatalogTrustSnapshotAbsent : CatalogTrustSnapshot { /// [JsonIgnore] - public override string Kind => "plugin"; + public override string Status => "absent"; - /// 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; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { 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; } + /// 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; } - /// 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; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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; } +/// 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"; - /// Canonical Agent Plugin media type. - [JsonPropertyName("mediaType")] - public required CatalogAgentPluginMediaType MediaType { 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; } - /// Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw data, or content digest. + /// Bounded source and observation time for this snapshot. This is distinct from evidence used by the authority to calculate trust. [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 required CatalogTrustProvenance Provenance { 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; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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. +/// Discriminator: the authority explicitly reported a downgraded assessment. +/// The downgraded variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogSearchPagination +public partial class CatalogTrustSnapshotDowngraded : CatalogTrustSnapshot { - /// One-based page returned by the authority. - [JsonPropertyName("currentPage")] - public int CurrentPage { get; set; } + /// + [JsonIgnore] + public override string Status => "downgraded"; - /// 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; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { 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; } + /// 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; } - /// 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; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } +} - /// Page size bound to the search, equal to the effective request limit. - [JsonPropertyName("pageSize")] - public int PageSize { get; set; } +/// Discriminator: the authority explicitly revoked the assessment. +/// The revoked variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogTrustSnapshotRevoked : CatalogTrustSnapshot +{ + /// + [JsonIgnore] + public override string Status => "revoked"; - /// 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; + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { get; set; } - /// 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; } + /// 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; } - /// 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; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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 . +/// 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 CatalogSearchResultSucceeded : CatalogSearchResult +public partial class CatalogTrustSnapshotUnsupported : CatalogTrustSnapshot { /// [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; } - - /// Protocol version and capabilities the runtime honoured. - [JsonPropertyName("negotiated")] - public required CatalogNegotiatedContract Negotiated { get; set; } + public override string Status => "unsupported"; - /// 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; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { 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; } + /// 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; } - /// 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; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { 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 . +/// Discriminator: the trust field was empty, unbounded, or had the wrong JSON type. +/// The malformed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultNegotiationRefused : CatalogSearchResult +public partial class CatalogTrustSnapshotMalformed : CatalogTrustSnapshot { /// [JsonIgnore] - public override string Kind => "negotiation-refused"; + public override string Status => "malformed"; - /// 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; } + /// Service-computed exposure eligibility. `unknown` is required while Agent Finder returns no explicit eligibility field. + [JsonPropertyName("eligibility")] + public required CatalogTrustEligibility Eligibility { 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; } + /// 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; } - /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. - [JsonPropertyName("unsupportedCapabilities")] - public required IList UnsupportedCapabilities { get; set; } + /// Schema version of this runtime-owned snapshot envelope. + [JsonPropertyName("schemaVersion")] + public required CatalogTrustSnapshotSchemaVersion SchemaVersion { get; set; } } -/// The request asked for a candidate kind this runtime does not serve. -/// The unsupported-kind variant of . +/// Catalogue identity retained from a bound candidate or plan at installation time. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultUnsupportedKind : CatalogSearchResult +public sealed class InstallationCatalogueIdentity { - /// - [JsonIgnore] - public override string Kind => "unsupported-kind"; + /// Catalogue description retained at install planning time. + [JsonPropertyName("description")] + public string? Description { 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; } + /// Human display name retained from the catalogue candidate. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// The kinds from the request that are not supported. - [JsonPropertyName("requestedKinds")] - public required IList RequestedKinds { get; set; } + /// Catalogue item URL when supplied by the authority. + [JsonPropertyName("itemUrl")] + public string? ItemUrl { get; set; } - /// Every candidate kind this runtime can serve. - [JsonPropertyName("supportedKinds")] - public required IList SupportedKinds { get; set; } + /// Catalogue publisher retained at install planning time. + [JsonPropertyName("publisher")] + public string? Publisher { get; set; } + + /// Authority resource identifier when supplied by the catalogue. + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } + + /// Catalogue authority/source string that supplied the candidate. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; + + /// Catalogue trust observation retained at install planning time. + [JsonPropertyName("trustAtInstall")] + public CatalogTrustSnapshot? TrustAtInstall { get; set; } + + /// Catalogue version retained at install planning time. + [JsonPropertyName("version")] + public string? Version { 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 . +/// Durable configuration ownership is distinct from session-specific usability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultInvalidRequest : CatalogSearchResult +public sealed class McpInstallationSummary { - /// - [JsonIgnore] - public override string Kind => "invalid-request"; + /// Catalogue identity retained from the installed plan when available. + [JsonPropertyName("catalogue")] + public InstallationCatalogueIdentity? Catalogue { get; set; } - /// Which request field was rejected. - [JsonPropertyName("field")] - public required CatalogInvalidRequestField Field { get; set; } + /// Exact alternative retained in the installing receipt. + [JsonPropertyName("choiceId")] + public string ChoiceId { 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; } + /// 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; } } -/// 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 . +/// The listed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultAuthenticationRequired : CatalogSearchResult +public partial class McpInstallationManagementOutcomeListed : McpInstallationManagementOutcome { /// [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; } + public override string Kind => "listed"; - /// 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; } + /// Owned receipts visible to the selected account and host. + [JsonPropertyName("installations")] + public required IList Installations { get; set; } } -/// Registry or enterprise policy refused the operation. -/// The policy-rejected variant of . +/// The recovered variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultPolicyRejected : CatalogSearchResult +public partial class McpInstallationManagementOutcomeRecovered : McpInstallationManagementOutcome { /// [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; } + public override string Kind => "recovered"; - /// Which authority produced the decision. - [JsonPropertyName("source")] - public required McpPlanPolicySource Source { get; set; } + /// Freshly inspected receipts after successful durable reconciliation. + [JsonPropertyName("installations")] + public required IList Installations { 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 . +/// Exact inert removal plan. No configuration or credentials have changed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultNetworkFailure : CatalogSearchResult +public sealed class McpUninstallPlan { - /// - [JsonIgnore] - public override string Kind => "network-failure"; + /// Original wall-clock expiry in milliseconds. Applying never renews it. + [JsonPropertyName("expiresAtEpochMs")] + public long ExpiresAtEpochMs { 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 owned receipt being removed. + [JsonPropertyName("installation")] + public McpInstallationSummary Installation { 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; } + /// The original operation, inspectable and cancellable on this same connection. + [JsonPropertyName("operationId")] + public string OperationId { 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; } + /// Exact configured input slots owned by this installation, never shared OAuth tokens. + [JsonPropertyName("ownedSecretCount")] + public long OwnedSecretCount { get; set; } - /// HTTP status code, when the failure was a rejected response. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("statusCode")] - public int? StatusCode { 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; } } -/// 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 . +/// The uninstall-planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultUnsafeRetrieval : CatalogSearchResult +public partial class McpInstallationManagementOutcomeUninstallPlanned : McpInstallationManagementOutcome { /// [JsonIgnore] - public override string Kind => "unsafe-retrieval"; + public override string Kind => "uninstall-planned"; - /// 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 owned removal plan and operation. + [JsonPropertyName("plan")] + public required McpUninstallPlan Plan { 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; } +/// 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(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 +{ + /// The type discriminator. + [JsonPropertyName("phase")] + public virtual string Phase { 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 . + +/// The preparing variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultMalformedCard : CatalogSearchResult +public partial class McpInstallationOperationStatusPreparing : McpInstallationOperationStatus { /// [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 Phase => "preparing"; - /// 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; } + /// Whether cancellation has been requested, not proof that a write was undone. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// How the card failed validation. - [JsonPropertyName("reason")] - public required CatalogMalformedCardReason Reason { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { 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 prepared variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultContractViolation : CatalogSearchResult +public partial class McpInstallationOperationStatusPrepared : McpInstallationOperationStatus { /// [JsonIgnore] - public override string Kind => "contract-violation"; + public override string Phase => "prepared"; - /// 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; } + /// Whether the inert prepared operation was asked to cancel. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// Which rule the response broke. - [JsonPropertyName("reason")] - public required CatalogContractViolationReason Reason { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. -/// The unavailable variant of . +/// The awaiting-confirmation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSearchResultUnavailable : CatalogSearchResult +public partial class McpInstallationOperationStatusAwaitingConfirmation : McpInstallationOperationStatus { /// [JsonIgnore] - public override string Kind => "unavailable"; + public override string Phase => "awaiting-confirmation"; - /// 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 the pending human callback was asked to cancel. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// Why the operation is unavailable. - [JsonPropertyName("reason")] - public required CatalogUnavailableReason Reason { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// 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. +/// The revalidating variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogSearchPage +public partial class McpInstallationOperationStatusRevalidating : McpInstallationOperationStatus { - /// 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; } + /// + [JsonIgnore] + public override string Phase => "revalidating"; - /// 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; + /// 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; } } -/// 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. +/// The applying variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CatalogSearchRequest +public partial class McpInstallationOperationStatusApplying : McpInstallationOperationStatus { - /// Protocol version and capabilities the caller requires. - [JsonPropertyName("contract")] - public required CatalogClientContract Contract { get; 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; } - - /// 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; } + /// + [JsonIgnore] + public override string Phase => "applying"; - /// - /// 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; } + /// Whether applying was asked to cancel; already-started effects retain their lease. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { 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; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// 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. +/// 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(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 +[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")] @@ -6163,165 +5924,200 @@ public partial class CatalogSelectionResult } -/// The chosen candidate was transferred into a fresh bounded single-use handle for a later explicit planning request. -/// The selected variant of . +/// The installed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultSelected : CatalogSelectionResult +public partial class McpInstallationOutcomeInstalled : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "selected"; + public override string Kind => "installed"; - /// 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; } + /// Durable installation succeeded but final transaction cleanup remains. + [JsonPropertyName("cleanupPending")] + public required bool CleanupPending { 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; } + /// Receipt identity produced by the confirmed transaction. + [JsonPropertyName("installation")] + public required McpInstallationSummary Installation { get; set; } } -/// The caller explicitly declined every candidate and the retained search state was released. -/// The declined variant of . +/// The uninstalled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultDeclined : CatalogSelectionResult +public partial class McpInstallationOutcomeUninstalled : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "declined"; + public override string Kind => "uninstalled"; - /// 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; } + /// 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; } } -/// The caller cancelled the selection interaction and the retained search state was released. -/// The cancelled variant of . +/// The durable transaction was aborted or fully compensated. +/// The rolled-back variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultCancelled : CatalogSelectionResult +public partial class McpInstallationOutcomeRolledBack : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "cancelled"; + public override string Kind => "rolled-back"; - /// 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; } + /// Original connection-owned operation. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } + + /// Cause of the fully aborted or compensated operation. + [JsonPropertyName("reason")] + public required McpInstallationFailureReason Reason { get; set; } } -/// The host declared that its live selection interaction timed out, and the retained search state was released. -/// The timed-out variant of . +/// 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 CatalogSelectionResultTimedOut : CatalogSelectionResult +public partial class McpInstallationOutcomeRecoveryRequired : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "timed-out"; + public override string Kind => "recovery-required"; - /// 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; } + /// Operation whose durable result must be recovered and inspected. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The selection reference was malformed or unknown. -/// The invalid variant of . +/// The declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultInvalid : CatalogSelectionResult +public partial class McpInstallationOutcomeDeclined : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "invalid"; + public override string Kind => "declined"; - /// 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; } + /// Original operation explicitly declined by the user. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The runtime-enforced selection reference lifetime elapsed before the request arrived. -/// The stale variant of . +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultStale : CatalogSelectionResult +public partial class McpInstallationOutcomeCancelled : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "stale"; + public override string Kind => "cancelled"; - /// 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; } + /// Original operation cancelled before a terminal application result. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The selection group was already terminated or its pending host hand-off was already claimed. -/// The replayed variant of . +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultReplayed : CatalogSelectionResult +public partial class McpInstallationOutcomeRefused : McpInstallationOutcome { /// [JsonIgnore] - public override string Kind => "replayed"; + public override string Kind => "refused"; - /// 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; } + /// Present once an operation has been allocated; never a plan handle. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operationId")] + public string? OperationId { get; set; } + + /// Specific bounded refusal, never a success-shaped fallback. + [JsonPropertyName("reason")] + public required McpInstallationFailureReason Reason { get; set; } } -/// The selection reference belongs to another runtime instance or session. -/// The foreign variant of . +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultForeign : CatalogSelectionResult +public partial class McpInstallationOperationStatusCompleted : McpInstallationOperationStatus { /// [JsonIgnore] - public override string Kind => "foreign"; + public override string Phase => "completed"; - /// 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; } + /// 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 McpInstallationOutcome Outcome { get; set; } } -/// The presented opaque handle was issued for another catalog operation. -/// The wrong-kind variant of . +/// The operation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultWrongKind : CatalogSelectionResult +public partial class McpInstallationManagementOutcomeOperation : McpInstallationManagementOutcome { /// [JsonIgnore] - public override string Kind => "wrong-kind"; + public override string Kind => "operation"; - /// 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; } + /// Original-connection operation snapshot. + [JsonPropertyName("operation")] + public required McpInstallationOperationStatus Operation { get; set; } +} + +/// The refused variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationManagementOutcomeRefused : McpInstallationManagementOutcome +{ + /// + [JsonIgnore] + public override string Kind => "refused"; + + /// Specific bounded refusal. + [JsonPropertyName("reason")] + public required McpInstallationFailureReason Reason { get; set; } +} + +/// The outcome variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpInstallationManagementResultOutcome : McpInstallationManagementResult +{ + /// + [JsonIgnore] + public override string Kind => "outcome"; + + /// Capabilities actually honoured for this request. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { 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 CatalogSelectionResultNegotiationRefused : CatalogSelectionResult +public partial class McpInstallationManagementResultNegotiationRefused : McpInstallationManagementResult { /// [JsonIgnore] @@ -6355,9 +6151,9 @@ public partial class CatalogSelectionResultNegotiationRefused : CatalogSelection } /// 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 CatalogSelectionResultInvalidRequest : CatalogSelectionResult +public partial class McpInstallationManagementResultInvalidRequest : McpInstallationManagementResult { /// [JsonIgnore] @@ -6374,664 +6170,458 @@ public partial class CatalogSelectionResultInvalidRequest : CatalogSelectionResu public required string Message { get; set; } } -/// The operation is not available on this runtime. Distinct from a network failure: nothing was attempted. -/// The unavailable variant of . +/// One Registry string-valued configuration entry for the selected transport. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class CatalogSelectionResultUnavailable : CatalogSelectionResult +public sealed class McpInstallationInput { - /// - [JsonIgnore] - public override string Kind => "unavailable"; + /// Exact category declared by the selected choice. + [JsonPropertyName("category")] + public McpPlanValueCategory Category { 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; } + /// Exact key declared by the selected choice. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Why the operation is unavailable. - [JsonPropertyName("reason")] - public required CatalogUnavailableReason Reason { get; set; } + /// Explicit non-secret value. Secret placeholders use a separate input channel. + [JsonPropertyName("value")] + public string Value { get; set; } = string.Empty; } -/// Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection. +/// A request-local value for one exact reviewed placeholder. Never logged or persisted in a plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CatalogSelectionRequest +public sealed class McpInstallationSecret { - /// 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; + /// Exact placeholder from the selected choice, not a caller-chosen backend identifier. + [JsonPropertyName("placeholder")] + public string Placeholder { 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; + /// Fresh explicit secret value. It is omitted from confirmation reviews and telemetry. + [JsonPropertyName("value")] + public string Value { get; set; } = string.Empty; } -/// Information about an installed plugin tracked in global state. +/// Side-effect-free preparation of one original bound remote MCP choice. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstalledPluginInfo +internal sealed class McpPrepareInstallRequest { - /// 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; } - - /// Whether the plugin is currently enabled for new sessions. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Exact selected alternative from that plan. + [JsonPropertyName("choiceId")] + public string ChoiceId { 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; } + /// Required bound catalogue and confirmed remote installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); 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; } + /// + /// 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; } - /// Whether enterprise managed settings control this plugin's enabled state. - [JsonPropertyName("managed")] - public bool? Managed { get; set; } + /// Original single-use bound plan, never a client-authored configuration. + [JsonPropertyName("planHandle")] + public string PlanHandle { 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; } + /// An existing local session attached to this connection, not permission to attach one. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { 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; + /// + /// 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; } - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The trusted host presents this choice alongside the exact secret placeholders. + [JsonPropertyName("secretStorage")] + public McpInstallationSecretStorage SecretStorage { 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. + /// The exact original source, used transiently only after confirmation. [JsonPropertyName("source")] - public string? Source { get; set; } + public McpServerCardReference Source { get => field ??= new(); set; } +} - /// Installed version (when reported by the plugin manifest). - [JsonPropertyName("version")] - public string? Version { get; set; } +/// 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)] +[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; } -/// Plugins installed in user/global state. + +/// The outcome variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginListResult +public partial class McpInstallationResultOutcome : McpInstallationResult { - /// Installed plugins. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "outcome"; + + /// Capabilities actually honoured for this request. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } + + /// Terminal result of the original operation. + [JsonPropertyName("outcome")] + public required McpInstallationOutcome Outcome { get; set; } } -/// Result of installing a plugin. +/// 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 PluginInstallResult +public partial class McpInstallationResultNegotiationRefused : McpInstallationResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// The newly installed plugin's metadata. - [JsonPropertyName("plugin")] - public InstalledPluginInfo Plugin { 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; } - /// Optional post-install message provided by the plugin (e.g. setup instructions). - [JsonPropertyName("postInstallMessage")] - public string? PostInstallMessage { get; set; } + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } - /// Number of skills discovered and installed from the plugin. - [JsonPropertyName("skillsInstalled")] - public long SkillsInstalled { get; set; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } - /// Where the completed plugin tree was staged before atomic promotion. - [JsonPropertyName("stagingMode")] - public PluginInstallStagingMode? StagingMode { 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; } } -/// Plugin source and optional working directory for relative-path resolution. +/// 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 PluginsInstallRequest +public partial class McpInstallationResultInvalidRequest : McpInstallationResult { - /// 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; + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// 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; } + /// 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; } } -/// Name (or spec) of the plugin to uninstall. +/// Applies exactly one previously prepared operation on its original connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUninstallRequest +internal sealed class McpApplyInstallRequest { - /// 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; } + /// Capabilities required by the original prepared operation. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); 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; + /// Runtime-issued ID already returned by prepareInstall, never reused or rebound. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; + + /// Same existing attached or privately borrowed session as preparation. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Result of updating a single plugin. +/// Read-only preparation of one owned removal under fresh selected-session authority. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginUpdateResult +internal sealed class McpPlanUninstallRequest { - /// Version after the update, when reported by the plugin manifest. - [JsonPropertyName("newVersion")] - public string? NewVersion { get; set; } + /// Required authenticated bound installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Version that was previously installed, when available. - [JsonPropertyName("previousVersion")] - public string? PreviousVersion { get; set; } + /// Exact receipt to inspect, not a server-name guess. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; - /// Number of skills discovered and installed after the update. - [JsonPropertyName("skillsInstalled")] - public long SkillsInstalled { get; set; } + /// Existing selected session on the original connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Name (or spec) of the plugin to update. +/// One-use application of the exact retained removal plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUpdateRequest +internal sealed class McpApplyUninstallRequest { - /// Plugin name or "plugin@marketplace" spec to update. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Required authenticated bound installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); 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; } -/// Per-plugin result from updating all plugins, with versions, skills installed, success flag, and optional error. +/// User-configured MCP servers, keyed by server name. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginUpdateAllEntry +public sealed class McpConfigList { - /// Error message (failure only). - [JsonPropertyName("error")] - public string? Error { get; set; } + /// All MCP servers from user config, keyed by name. + [JsonPropertyName("servers")] + public IDictionary Servers { get => field ??= new Dictionary(); set; } +} - /// Marketplace the plugin came from. Empty string ("") for direct installs. - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; +/// MCP server name and configuration to add to user configuration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpConfigAddRequest +{ + /// MCP server configuration (stdio process or remote HTTP/SSE). + [JsonPropertyName("config")] + public JsonElement Config { get; set; } - /// Plugin name that was updated. + /// 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)] [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; +} - /// Version after the update, when available. - [JsonPropertyName("newVersion")] - public string? NewVersion { get; set; } - - /// Previously installed version, when available. - [JsonPropertyName("previousVersion")] - public string? PreviousVersion { get; set; } - - /// Number of skills installed after the update (success only). - [JsonPropertyName("skillsInstalled")] - public long? SkillsInstalled { get; set; } +/// MCP server name and replacement configuration to write to user configuration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpConfigUpdateRequest +{ + /// MCP server configuration (stdio process or remote HTTP/SSE). + [JsonPropertyName("config")] + public JsonElement Config { get; set; } - /// Whether the update succeeded for this plugin. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// 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)] + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Result of updating all installed plugins. +/// MCP server name to remove from user configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginUpdateAllResult +internal sealed class McpConfigRemoveRequest { - /// Per-plugin update results in deterministic order. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; set; } + /// OAuth Client ID Metadata Document URL whose persisted credentials should also be removed. + [JsonPropertyName("authClientIdMetadataUrl")] + public string? AuthClientIdMetadataUrl { get; set; } + + /// 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)] + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. +/// MCP server names to enable for new sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsEnableRequest +internal sealed class McpConfigEnableRequest { - /// 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. + /// 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; } - - /// 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; } } -/// Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. +/// MCP server names to disable for new sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsDisableRequest +internal sealed class McpConfigDisableRequest { - /// 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. + /// 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; } +} - /// 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; } +/// Request id naming a cancellable registry search. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class 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. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// Trusted built-in plugin directories to use for this runtime process. +/// Servers selected from the registry response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsBuiltinSetRequest +internal sealed class McpRegistrySearchResult { - /// 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 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; } } -/// Registered marketplace summary. +/// Registry search terms, the credential to search under, and the request id that makes the search cancellable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceInfo +internal sealed class McpRegistrySearchRequest { - /// 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; } + /// 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; } - /// 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 servers to return. + [JsonPropertyName("limit")] + public long Limit { 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; -} - -/// All registered marketplaces, including built-in defaults. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceListResult -{ - /// Registered marketplaces. - [JsonPropertyName("marketplaces")] - public IList Marketplaces { get => field ??= []; set; } -} - -/// Result of registering a new marketplace. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceAddResult -{ - /// Final name of the marketplace as resolved from its manifest. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} + /// 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; } -/// Marketplace source and optional working directory for relative-path resolution. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesAddRequest -{ - /// 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; + /// 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; } - /// 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; } + /// 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; } } -/// Outcome of the remove attempt, including dependent-plugin info when applicable. +/// Whether the cancel reached a live search. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceRemoveResult +internal sealed class McpRegistryCancelResult { - /// 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; } + /// 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; } } -/// Name of the marketplace to remove and an optional force flag. +/// Registry search to abandon. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesRemoveRequest +internal sealed class McpRegistryCancelRequest { - /// 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; + /// Request ID from `mcp.registry.allocateRequestId` that the search uses. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// Plugin entry advertised by a marketplace. +/// New-work inventory or recovery request under an explicitly selected existing session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplacePluginInfo +internal sealed class McpInstallationsRequest { - /// Short description from the marketplace catalog, when present. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Plugin name as listed in the marketplace catalog. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Existing selected local session on this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Plugins advertised by the marketplace. +/// Existing-operation control. A new session selector is deliberately not accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceBrowseResult +internal sealed class McpInstallationOperationRequest { - /// Plugins advertised by the marketplace. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } -} + /// Original installation wire capability; new-work authentication is not reacquired. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); 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; + /// Exact runtime-issued operation ID on the original connection. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; } -/// Per-marketplace refresh result, including marketplace name, success flag, and optional failure error. +/// Installed plugin that contributes a discovered extension. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MarketplaceRefreshEntry +public sealed class DiscoveredExtensionPlugin { - /// Error message (failure only). - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Marketplace name that was refreshed. + /// Installed plugin name. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - - /// Whether the refresh succeeded. - [JsonPropertyName("success")] - public bool Success { 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; } -} - -/// 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; } -} - -/// Agent metadata, including identifiers, display details, source, tools, model, models, reasoning effort, MCP servers, skills, and file path. +/// Discovered extension metadata and persistent enablement state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentInfo +public sealed class DiscoveredExtension { - /// 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; + /// Whether this extension's persistent per-ID preference is enabled. + [JsonPropertyName("enabled")] + public bool Enabled { 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. + /// Source-qualified ID accepted by both server and session extension enablement methods. [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. + /// Human-readable extension 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; } - - /// 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; } -} - -/// Agents discovered across user, project, plugin, and remote sources. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerAgentList -{ - /// All discovered agents across all sources. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } -} - -/// 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; } - - /// 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; } -} - -/// Canonical directory where custom agents can be discovered or created, with scope, preference, and optional project path. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentDiscoveryPath -{ - /// Absolute path of the search/create directory (may not exist on disk yet). + /// Absolute path to the extension entry module, suitable for revealing it in a file manager. [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; } -} - -/// Canonical locations where custom agents can be created so the runtime will recognize them. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentDiscoveryPathList -{ - /// Canonical agent create/discovery directories, in priority order. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } -} - -/// Optional project paths to include when enumerating agent discovery directories. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetDiscoveryPathsRequest -{ - /// 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; } -} - -/// 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 AgentsGetBuiltinsResult -{ - /// 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; } -} - -/// A shipped agent, named and described. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class BuiltinAgentSummary -{ - /// One-line description of what the agent does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// The agent name, as it appears in `getBuiltins`. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} - -/// 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; } -} - -/// The feature flags to evaluate shipped agents against. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetAvailableBuiltinsRequest -{ - /// 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; } - - /// 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; } - - /// 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; } -} + /// Containing plugin metadata for plugin-contributed extensions. + [JsonPropertyName("plugin")] + public DiscoveredExtensionPlugin? Plugin { get; set; } -/// One shipped agent's definition. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -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; + /// Discovery source. + [JsonPropertyName("source")] + public DiscoveredExtensionSource Source { get; set; } } -/// The shipped agent whose definition to load. +/// 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)] -internal sealed class AgentsGetBuiltinDefinitionRequest +public sealed class DiscoveredExtensions { - /// 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; -} + /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state. + [JsonPropertyName("extensions")] + public IList Extensions { get => field ??= []; set; } -/// One shipped agent, projected for a listing. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -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; + /// Effective extension loading mode. Defaults to load_and_augment when unset. + [JsonPropertyName("mode")] + public DiscoveredExtensionMode Mode { get; set; } } -/// The shipped agent whose listing entry to load. +/// Source-qualified extension identifiers to persistently enable for future sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsGetBuiltinListingDefinitionRequest +internal sealed class DiscoveredExtensionsEnableRequest { - /// 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; + /// Source-qualified user or plugin extension IDs to enable. + [JsonPropertyName("ids")] + public IList Ids { get => field ??= []; set; } } -/// The model to switch to, and the warning to show when the agent's preference could not be met. +/// Source-qualified extension identifiers to persistently disable for future sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsCustomAgentInitialModelDecisionResult +internal sealed class DiscoveredExtensionsDisableRequest { - /// 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; } - - /// 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; } + /// Source-qualified user or plugin extension IDs to disable. + [JsonPropertyName("ids")] + public IList Ids { get => field ??= []; set; } } -/// The models a custom agent asks for, and the models actually available. +/// The prompt the init command runs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentsCustomAgentInitialModelDecisionParams +internal sealed class InstructionsInitPromptResult { - /// 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; + /// Prompt text authoring repository instructions. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; } /// Loaded instruction source for a session, including path, content, category, location, applicability, and optional description. @@ -7090,17 +6680,50 @@ public sealed class ServerInstructionSourceList /// Optional project paths to include in instruction discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class InstructionsDiscoverRequest +public sealed 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. [JsonPropertyName("excludeHostInstructions")] public bool? ExcludeHostInstructions { 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; } + /// 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; } } +/// 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 InstructionsDiscoverRepoSourcesRequest +{ + /// Extra directories to scan alongside instruction directories configured in the runtime environment. + [JsonPropertyName("additionalDirs")] + public IList? AdditionalDirs { 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; } + + /// Working directory whose instruction sources should also be included. Omission adds no separate working-directory scope. + [JsonPropertyName("cwdPath")] + public string? CwdPath { get; set; } + + /// When true, also discover instruction files nested below the location. + [JsonPropertyName("enableChildInstructions")] + public bool? EnableChildInstructions { 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; } + + /// 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; +} + /// 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)] public sealed class InstructionDiscoveryPath @@ -7137,822 +6760,877 @@ public sealed class InstructionDiscoveryPathList /// Optional project paths to include when enumerating instruction discovery targets. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class InstructionsGetDiscoveryPathsRequest +public sealed class InstructionsGetDiscoveryPathsRequest { /// 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; } + /// 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; } + /// Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. [JsonPropertyName("projectPaths")] public IList? ProjectPaths { get; set; } } -/// Installed plugin record from global state, with marketplace, version, install time, enabled state, cache path, and source. +/// Skill installation management result with the honoured contract, or a typed refusal. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstalledPlugin +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationManagementResultOutcome), "outcome")] +[JsonDerivedType(typeof(SkillInstallationManagementResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(SkillInstallationManagementResultInvalidRequest), "invalid-request")] +public partial class SkillInstallationManagementResult { - /// 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. - [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; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - /// Source for direct repo installs (when marketplace is empty). - [JsonPropertyName("source")] - public JsonElement? Source { get; set; } +/// Management outcome for verified Skill inventory, planning and removal. +/// Polymorphic base type discriminated by kind. +[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 +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } - /// Version installed (if available). - [JsonPropertyName("version")] - public string? Version { get; set; } +/// 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 SkillInstallationManagementOutcomeRecoveryRequired : SkillInstallationManagementOutcome +{ + /// + [JsonIgnore] + public override string Kind => "recovery-required"; } -/// 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. +/// 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)] -internal sealed class LoggedInUser +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "action", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SkillInstallationReviewInstall), "install")] +[JsonDerivedType(typeof(SkillInstallationReviewUninstall), "uninstall")] +public partial class SkillInstallationReview { - /// Source account this account was derived from, when one was recorded. - [JsonPropertyName("derivedFrom")] - public string? DerivedFrom { get; set; } + /// The type discriminator. + [JsonPropertyName("action")] + public virtual string Action { get; set; } = string.Empty; +} - /// 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; } +/// One reviewed Skill file. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SkillInstallationFileReview +{ + /// SHA-256 digest of the exact file bytes. + [JsonPropertyName("digest")] + public string Digest { get; set; } = string.Empty; - /// Account login on that host. - [JsonPropertyName("login")] - public string Login { 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 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. +/// Source identity retained from Agent Finder and the pinned GitHub descriptor. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GlobalStateLoadResult +public sealed class SkillInstallationSource { - /// Whether the app tip has been shown. - [JsonPropertyName("appTipShown")] - public bool? AppTipShown { get; set; } + /// Digest of the descriptor's bundle manifest. + [JsonPropertyName("bundleDigest")] + public string BundleDigest { 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; } + /// Agent Finder materialisation revision identifier. + [JsonPropertyName("catalogRevisionId")] + public string CatalogRevisionId { 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; } + /// Digest of the canonical materialisation descriptor. + [JsonPropertyName("descriptorDigest")] + public string DescriptorDigest { get; set; } = string.Empty; - /// When the host first ran on this machine. - [JsonPropertyName("firstLaunchAt")] - public string? FirstLaunchAt { get; set; } + /// Repository full name, for example owner/name. + [JsonPropertyName("repository")] + public string Repository { get; set; } = string.Empty; - /// Plugins installed on this machine. - [JsonPropertyName("installedPlugins")] - public IList? InstalledPlugins { get; set; } + /// GitHub repository database identifier. + [JsonPropertyName("repositoryId")] + public string RepositoryId { get; set; } = string.Empty; - /// Account used for the most recent sign-in. - [JsonPropertyName("lastLoggedInUser")] - public LoggedInUser? LastLoggedInUser { get; set; } + /// Agent Finder resource identifier. + [JsonPropertyName("resourceId")] + public string ResourceId { get; set; } = string.Empty; - /// Every account the host has signed in to on this machine. - [JsonPropertyName("loggedInUsers")] - public IList? LoggedInUsers { get; set; } + /// Pinned Git commit revision. + [JsonPropertyName("revision")] + public string Revision { get; set; } = string.Empty; - /// Whether the one-off cleanup of stored reasoning summaries has run. - [JsonPropertyName("reasoningSummariesCleanupDone")] - public bool? ReasoningSummariesCleanupDone { get; set; } + /// Root path within the pinned repository. + [JsonPropertyName("root")] + public string Root { get; set; } = string.Empty; +} - /// Models the user selected recently, most recent first. - [JsonPropertyName("recentModelIds")] - public IList? RecentModelIds { 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; } - /// Whether the user declined to trust the sandbox credential proxy CA. - [JsonPropertyName("sandboxCredentialProxyCaDeclined")] - public bool? SandboxCredentialProxyCaDeclined { get; set; } + /// Safe display label, for example ~/.copilot/skills/run-checks. + [JsonPropertyName("displayLabel")] + public string DisplayLabel { get; set; } = string.Empty; - /// Whether the sandbox onboarding has been shown. - [JsonPropertyName("sandboxOnboardingShown")] - public bool? SandboxOnboardingShown { get; set; } + /// Path relative to the Copilot home. + [JsonPropertyName("relativePath")] + public string RelativePath { get; set; } = string.Empty; - /// Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only behavior. - [JsonPropertyName("staff")] - public bool? Staff { get; set; } + /// Installation scope. Agent Finder Skills are installed in the user's personal Copilot home. + [JsonPropertyName("scope")] + public SkillInstallationScope Scope { get; set; } +} - /// Whether the user was recognized as GitHub staff. - [JsonPropertyName("staffGithub")] - public bool? StaffGitHub { get; set; } +/// Review for installing a verified Skill. +/// The install variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationReviewInstall : SkillInstallationReview +{ + /// + [JsonIgnore] + public override string Action => "install"; - /// When the staff-only log level migration last ran. - [JsonPropertyName("staffLogLevelMigrationAt")] - public string? StaffLogLevelMigrationAt { get; set; } + /// Catalogue identity retained from the bound candidate before consent. + [JsonPropertyName("catalogue")] + public required InstallationCatalogueIdentity Catalogue { get; set; } - /// Whether the user was recognized as Microsoft staff. - [JsonPropertyName("staffMicrosoft")] - public bool? StaffMicrosoft { get; set; } + /// Skill description from SKILL.md when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// When the staff-only model reset last ran. - [JsonPropertyName("staffModelResetAt")] - public string? StaffModelResetAt { 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; } - /// When the staff-only update channel migration last ran. - [JsonPropertyName("staffUpdateChannelMigrationAt")] - public string? StaffUpdateChannelMigrationAt { get; set; } + /// Relative path of the verified Skill entrypoint. + [JsonPropertyName("entrypointPath")] + public required string EntrypointPath { get; set; } - /// Folders where the user declined the init prompt, so it stays hidden there. - [JsonPropertyName("suppressInitFolders")] - public IList? SuppressInitFolders { get; set; } + /// Reviewed files and digests. + [JsonPropertyName("files")] + public required IList Files { get; set; } - /// Folders the user has marked as trusted. - [JsonPropertyName("trustedFolders")] - public IList? TrustedFolders { get; set; } -} + /// Installing never grants immediate use; the Skill is written disabled. + [JsonPropertyName("installsDisabled")] + public required bool InstallsDisabled { get; set; } -/// Selects the configuration directory whose machine-wide state to read. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -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; } -} + /// Skill invocation name from SKILL.md. + [JsonPropertyName("name")] + public required string Name { 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. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GlobalStateWriteKeyRequest -{ - /// 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; } + /// Exact verified source identity. + [JsonPropertyName("source")] + public required SkillInstallationSource Source { get; set; } - /// 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; + /// Exact user-scope target location without an absolute host path. + [JsonPropertyName("target")] + public required SkillInstallationLocation Target { 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; } + /// Total reviewed payload size in bytes. + [JsonPropertyName("totalBytes")] + public required long TotalBytes { get; set; } } -/// A literal choice the command input accepts, with a human-facing description. +/// Durable verified Skill ownership summary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandInputChoice +public sealed class SkillInstallationSummary { - /// Human-readable description shown alongside the choice. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Catalogue identity retained at install time. + [JsonPropertyName("catalogue")] + public InstallationCatalogueIdentity Catalogue { get => field ??= new(); set; } - /// The literal choice value (e.g. 'on', 'off', 'show'). + /// Persisted enablement requested for this installation. + [JsonPropertyName("configuredEnabled")] + public bool ConfiguredEnabled { get; set; } + + /// Exact durable installation receipt identity. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; + + /// ISO 8601 wall-clock installation time. + [JsonPropertyName("installedAt")] + public string InstalledAt { get; set; } = string.Empty; + + /// Skill invocation name. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; -} -/// Optional unstructured input hint. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandInput -{ - /// 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; } + /// Operation that installed this Skill. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; - /// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). - [JsonPropertyName("completion")] - public SlashCommandInputCompletion? Completion { get; set; } + /// Ownership state observed from files and receipts. + [JsonPropertyName("ownershipState")] + public SkillInstallationOwnershipState OwnershipState { get; set; } - /// Hint to display when command input has not been provided. - [JsonPropertyName("hint")] - public string Hint { get; set; } = string.Empty; + /// Bound-session load observation. + [JsonPropertyName("sessionState")] + public SkillInstallationSessionState SessionState { get; set; } - /// 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; } + /// Exact retained verified source identity. + [JsonPropertyName("source")] + public SkillInstallationSource Source { 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; } + /// User-facing installation location without an absolute host path. + [JsonPropertyName("target")] + public SkillInstallationLocation Target { get => field ??= new(); set; } } -/// Slash-command metadata with name, aliases, description, kind, input hint, execution allowance, and schedulability. +/// Review for uninstalling an owned verified Skill. +/// The uninstall variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandInfo +public partial class SkillInstallationReviewUninstall : SkillInstallationReview { - /// Canonical aliases without leading slashes. - [JsonPropertyName("aliases")] - public IList? Aliases { get; 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; + /// + [JsonIgnore] + public override string Action => "uninstall"; - /// Whether the command is experimental. - [JsonPropertyName("experimental")] - public bool? Experimental { get; set; } + /// Catalogue identity retained at install time. + [JsonPropertyName("catalogue")] + public required InstallationCatalogueIdentity Catalogue { get; set; } - /// Optional unstructured input hint. - [JsonPropertyName("input")] - public SlashCommandInput? Input { get; set; } + /// Files recorded by the installation receipt. + [JsonPropertyName("files")] + public required IList Files { 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 current files differ from the receipt. Apply refuses drift. + [JsonPropertyName("filesModified")] + public required bool FilesModified { get; set; } - /// Canonical command name without a leading slash. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Owned installation being removed. + [JsonPropertyName("installation")] + public required SkillInstallationSummary Installation { get; set; } - /// 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; } + /// Total receipt-owned payload size in bytes. + [JsonPropertyName("totalBytes")] + public required long TotalBytes { get; set; } } -/// Slash commands available in the session, after applying any include/exclude filters. +/// A computed Skill install plan. Nothing has been applied. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandList +public sealed class SkillInstallPlan { - /// Commands available in this session. - [JsonPropertyName("commands")] - public IList Commands { get => field ??= []; set; } -} + /// Original wall-clock expiry as an ISO 8601 timestamp. + [JsonPropertyName("expiresAt")] + public string ExpiresAt { get; set; } = string.Empty; -/// 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 UserSettingMetadata -{ - /// The centrally-known default for this setting (null when no default is registered). - [JsonPropertyName("default")] - public JsonElement Default { get; set; } + /// Original operation identifier returned before confirmation. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; - /// 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; } + /// One-use plan handle, bound to the original candidate authority. + [JsonPropertyName("planHandle")] + public string PlanHandle { get; set; } = string.Empty; - /// The effective value: the user's value if set, otherwise the default. - [JsonPropertyName("value")] - public JsonElement Value { get; set; } + /// Safe review to present before applying the plan. + [JsonPropertyName("review")] + public SkillInstallationReview Review { get => field ??= new(); set; } } -/// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. +/// A verified Skill installation plan was prepared. +/// The install-planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UserSettingsGetResult +public partial class SkillInstallationManagementOutcomeInstallPlanned : SkillInstallationManagementOutcome { - /// 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; } -} + /// + [JsonIgnore] + public override string Kind => "install-planned"; -/// 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)] -internal sealed class UserSettingsSetRequest -{ - /// Partial user settings to write, as a free-form object keyed by setting name. - [JsonPropertyName("settings")] - public JsonElement Settings { get; set; } + /// Prepared install plan. + [JsonPropertyName("plan")] + public required SkillInstallPlan Plan { get; set; } } -/// Owner, name, and host of a GitHub repository, as resolved from a git remote URL. +/// Owned Skill installations were listed. +/// The listed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubRepositoryIdentity +public partial class SkillInstallationManagementOutcomeListed : SkillInstallationManagementOutcome { - /// 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; + /// + [JsonIgnore] + public override string Kind => "listed"; - /// Repository owner login (user or organization). - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Owned Skill installation summaries. + [JsonPropertyName("installations")] + public required IList Installations { get; set; } } -/// The GitHub repository that owns the requested path, when the selected remote (`origin`, else the first) is on a GitHub host. +/// Recovery completed and inventory was inspected. +/// The recovered variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubRepositoryAtPathResult +public partial class SkillInstallationManagementOutcomeRecovered : SkillInstallationManagementOutcome { - /// Resolved repository identity, or null when the selected remote resolves to no GitHub host. - [JsonPropertyName("repository")] - public GitHubRepositoryIdentity? Repository { get; set; } -} + /// + [JsonIgnore] + public override string Kind => "recovered"; -/// Working-tree path whose owning GitHub repository should be resolved. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubRepositoryAtPathRequest -{ - /// 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; + /// Owned Skill installation summaries after recovery. + [JsonPropertyName("installations")] + public required IList Installations { 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. +/// 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 GitHubOwnersRequestIdResult +public partial class SkillInstallationManagementOutcomeRolledBack : SkillInstallationManagementOutcome { - /// Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. - [JsonPropertyName("requestId")] - public long RequestId { get; set; } -} + /// + [JsonIgnore] + public override string Kind => "rolled-back"; -/// A GitHub login the authenticated user may act as: their own account, or an organization they belong to. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnerOption -{ - /// The owner's GitHub login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Original operation identity. + [JsonPropertyName("operation_id")] + public required string OperationId { get; 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; + /// Cause of the compensation. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { get; set; } } -/// 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. +/// A computed Skill uninstall plan. Nothing has been removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersListResult +public sealed class SkillUninstallPlan { - /// 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; } + /// Original wall-clock expiry as an ISO 8601 timestamp. + [JsonPropertyName("expiresAt")] + public string ExpiresAt { get; set; } = string.Empty; - /// The owners, on success: the authenticated user first, then the organizations they belong to. - [JsonPropertyName("owners")] - public IList? Owners { get; set; } + /// Owned Skill installation being removed. + [JsonPropertyName("installation")] + public SkillInstallationSummary Installation { get => field ??= new(); 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; } + /// Original removal operation identifier returned before confirmation. + [JsonPropertyName("operationId")] + public string OperationId { 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; } + /// 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; } } -/// Credential to list owners under, and the request id that makes the listing cancellable. +/// An owned Skill uninstall plan was prepared. +/// The uninstall-planned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersListRequest +public partial class SkillInstallationManagementOutcomeUninstallPlanned : SkillInstallationManagementOutcome { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "uninstall-planned"; - /// 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; } + /// Prepared uninstall plan. + [JsonPropertyName("plan")] + public required SkillUninstallPlan Plan { get; set; } } -/// Whether the id named a running owner listing. +/// 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 GitHubOwnersCancelResult +[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 { - /// 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 type discriminator. + [JsonPropertyName("phase")] + public virtual string Phase { get; set; } = string.Empty; } -/// The owner listing to abandon. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitHubOwnersCancelRequest -{ - /// Request id the listing was started with. - [JsonPropertyName("requestId")] - public long RequestId { get; set; } -} -/// The remote the checked-out branch tracks. +/// Original operation progress discriminator. +/// The preparing variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitCurrentBranchRemoteResult +public partial class SkillInstallationOperationStatusPreparing : SkillInstallationOperationStatus { - /// 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; + /// + [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; } } -/// Working-tree path a git query applies to. +/// Original operation progress discriminator. +/// The prepared variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitCwdRequest +public partial class SkillInstallationOperationStatusPrepared : SkillInstallationOperationStatus { - /// 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; + /// + [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; } } -/// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. +/// Original operation progress discriminator. +/// The awaiting-confirmation variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionWorkingDirectoryContext +public partial class SkillInstallationOperationStatusAwaitingConfirmation : SkillInstallationOperationStatus { - /// 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; + /// + [JsonIgnore] + public override string Phase => "awaiting-confirmation"; - /// Root directory of the git repository, resolved via git rev-parse. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// Whether cancellation has been requested. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// Head commit of the current git branch. - [JsonPropertyName("headCommit")] - public string? HeadCommit { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } +} - /// Hosting platform type of the repository. - [JsonPropertyName("hostType")] - public SessionWorkingDirectoryContextHostType? HostType { get; set; } +/// Original operation progress discriminator. +/// The revalidating variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationOperationStatusRevalidating : SkillInstallationOperationStatus +{ + /// + [JsonIgnore] + public override string Phase => "revalidating"; - /// 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; } + /// Whether cancellation has been requested. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// Raw host string from the git remote URL (e.g. "github.com", "dev.azure.com"). - [JsonPropertyName("repositoryHost")] - public string? RepositoryHost { get; set; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// A GitHub repository one of a working tree's remotes points at. +/// Original operation progress discriminator. +/// The applying variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitRemoteRepository +public partial class SkillInstallationOperationStatusApplying : SkillInstallationOperationStatus { - /// 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; + /// + [JsonIgnore] + public override string Phase => "applying"; - /// Account or organization owning the repository. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Whether cancellation has been requested; already-started effects require recovery. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { get; set; } - /// 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; + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// The GitHub repositories a working tree's remotes point at. +/// Terminal verified Skill mutation result. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitReposFromRemotesResult +[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 { - /// 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; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Git working tree whose GitHub remotes should be listed. + +/// The verified Skill was installed disabled. +/// The installed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class GitReposFromRemotesRequest +public partial class SkillInstallationOutcomeInstalled : SkillInstallationOutcome { - /// 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; + /// + [JsonIgnore] + public override string Kind => "installed"; + + /// Durable installed Skill summary. + [JsonPropertyName("installation")] + public required SkillInstallationSummary Installation { get; set; } } -/// Validated device-managed settings discovered before a session exists. +/// The owned Skill was removed. +/// The uninstalled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsReadResult +public partial class SkillInstallationOutcomeUninstalled : SkillInstallationOutcome { - /// Discovery or validation error text when managed settings could not be read safely. - [JsonPropertyName("errorMessage")] - public string? ErrorMessage { get; set; } + /// + [JsonIgnore] + public override string Kind => "uninstalled"; - /// 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; } + /// Removed installation identity. + [JsonPropertyName("installationId")] + public required string InstallationId { get; set; } + + /// Original removal operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// One validation finding for a managed-settings document. +/// The durable transaction was aborted or fully compensated. +/// The rolled-back variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsDiagnostic +public partial class SkillInstallationOutcomeRolledBack : SkillInstallationOutcome { - /// Human-readable description of the finding. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "rolled-back"; - /// 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; + /// Original operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } - /// Whether the finding rejects the document. - [JsonPropertyName("severity")] - public ManagedSettingsDiagnosticSeverity Severity { get; set; } + /// Cause of the fully aborted or compensated operation. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { get; set; } } -/// One managed-settings channel and the document it delivered. +/// A write may have completed. Recover and inspect durable state before retrying. +/// The recovery-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsLayer +public partial class SkillInstallationOutcomeRecoveryRequired : SkillInstallationOutcome { - /// Validated managed-settings document this channel delivered. Absent when the channel delivered none. - [JsonPropertyName("settings")] - public JsonElement? Settings { get; set; } + /// + [JsonIgnore] + public override string Kind => "recovery-required"; - /// 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; + /// Operation whose durable result must be recovered and inspected. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// Lock state and provenance of one managed setting. +/// The user declined the confirmation request. +/// The declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingMeta +public partial class SkillInstallationOutcomeDeclined : SkillInstallationOutcome { - /// Whether users and repositories may choose a different value. `false` means policy locks the value. - [JsonPropertyName("overridable")] - public bool Overridable { 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; } + /// + [JsonIgnore] + public override string Kind => "declined"; - /// 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; + /// Original operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { 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. +/// The operation was cancelled before a terminal mutation. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsMeta +public partial class SkillInstallationOutcomeCancelled : SkillInstallationOutcome { - /// 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; } - - /// Lock state and provenance of `values.effortLevel`. - [JsonPropertyName("effortLevel")] - public ManagedSettingMeta? EffortLevel { get; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Lock state and provenance of `values.model`. - [JsonPropertyName("model")] - public ManagedSettingMeta? Model { get; set; } + /// Original operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { get; set; } } -/// 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. +/// The operation was refused without applying changes. +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsResolvedData +public partial class SkillInstallationOutcomeRefused : SkillInstallationOutcome { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "refused"; - /// Whether a session-local permissions layer injected by the SDK host was present. - [JsonPropertyName("clientManaged")] - public bool? ClientManaged { get; set; } + /// Present once an operation has been allocated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operationId")] + public string? OperationId { get; set; } - /// Whether an actual device MDM/plist/registry/file managed-settings layer was present. - [JsonPropertyName("deviceManaged")] - public bool DeviceManaged { get; set; } + /// Bounded refusal reason. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { 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; } +/// Original operation progress discriminator. +/// The completed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationOperationStatusCompleted : SkillInstallationOperationStatus +{ + /// + [JsonIgnore] + public override string Phase => "completed"; - /// 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; } + /// Whether cancellation was requested before the terminal result. + [JsonPropertyName("cancellationRequested")] + public required bool CancellationRequested { 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; } + /// Original runtime-issued operation identity. + [JsonPropertyName("operationId")] + public required string OperationId { 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; } + /// Immutable terminal receipt. + [JsonPropertyName("outcome")] + public required SkillInstallationOutcome Outcome { 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; } +/// 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"; - /// 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; } + /// Operation status. + [JsonPropertyName("operation")] + public required SkillInstallationOperationStatus Operation { get; set; } +} - /// Whether the server (account/org) managed-settings layer was present. - [JsonPropertyName("serverManaged")] - public bool ServerManaged { 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"; - /// 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; } + /// Safe reload or reconciliation diagnostics. + [JsonPropertyName("diagnostics")] + public required IList Diagnostics { 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; } + /// Updated installation summary. + [JsonPropertyName("installation")] + public required SkillInstallationSummary Installation { 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. +/// The management request was refused. +/// The refused variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsValues +public partial class SkillInstallationManagementOutcomeRefused : SkillInstallationManagementOutcome { - /// Managed Auto routing preference, used when the selected model is `auto`. - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } + /// + [JsonIgnore] + public override string Kind => "refused"; - /// Managed context-tier default for the managed concrete model. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } + /// Bounded refusal reason. + [JsonPropertyName("reason")] + public required SkillInstallationFailureReason Reason { 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; } +/// The outcome variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SkillInstallationManagementResultOutcome : SkillInstallationManagementResult +{ + /// + [JsonIgnore] + public override string Kind => "outcome"; - /// 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; } + /// Capabilities honoured for this request. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } + + /// Observed management outcome. + [JsonPropertyName("outcome")] + public required SkillInstallationManagementOutcome Outcome { get; set; } } -/// Effective enterprise managed settings for an account, resolved without a 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 ManagedSettingsResolveResult +public partial class SkillInstallationManagementResultNegotiationRefused : SkillInstallationManagementResult { - /// 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 => "negotiation-refused"; - /// 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; } + /// 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; } - /// 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; } + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { 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; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; 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; } + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { 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; } + /// 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; } - /// 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; } + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } } -/// RPC data type for ManagedSettingsResolve operations. +/// 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 ManagedSettingsResolveRequest +public partial class SkillInstallationManagementResultInvalidRequest : SkillInstallationManagementResult { - /// Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. - [JsonPropertyName("clientName")] - public string? ClientName { 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; } + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// 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; } + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { 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; } + /// 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 authoring JSON schema for managed settings recognized by this runtime. +/// Side-effect-free planning of one verified Agent Finder Skill candidate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsSchemaResult +internal sealed class SkillPlanInstallRequest { - /// Version of the runtime that owns this schema. - [JsonPropertyName("runtimeVersion")] - public string RuntimeVersion { get; set; } = string.Empty; + /// Fresh single-use AI skill candidate handle returned by a bound catalogue search. + [JsonPropertyName("candidateHandle")] + public string CandidateHandle { get; set; } = string.Empty; - /// 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; } + /// 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; } -/// Result of validating a managed-settings document. +/// +/// An Agent Finder materialisation reference pinning one exact resource revision. +/// +/// It carries no origin or URL: the runtime always builds the descriptor URL on its fixed Agent +/// Finder authority. It carries no display data either; every reviewed field comes from the +/// verified descriptor and Skill files. +/// [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsValidateResult +public sealed class CatalogMaterializationReference { - /// 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; } + /// Expected `sha256:<lowercase hex>` digest of the descriptor's bundle manifest. + [JsonPropertyName("bundleDigest")] + public string BundleDigest { get; set; } = string.Empty; - /// 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; } -} - -/// A candidate managed-settings document to validate without applying it. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ManagedSettingsValidateRequest -{ - /// 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; } - - /// 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; } -} - -/// 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 ManagedSettingsComposeResult -{ - /// Warnings about ignored content, with paths prefixed by the channel name. - [JsonPropertyName("diagnostics")] - public IList Diagnostics { get => field ??= []; 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; } + /// Expected `sha256:<lowercase hex>` digest of the canonical descriptor. + [JsonPropertyName("descriptorDigest")] + public string DescriptorDigest { get; set; } = string.Empty; - /// Per-key lock state and provenance for `values`. - [JsonPropertyName("meta")] - public ManagedSettingsMeta? Meta { get; set; } + /// Agent Finder resource identifier, a positive decimal integer. + [JsonPropertyName("resourceId")] + public string ResourceId { get; set; } = string.Empty; - /// Effective managed settings, in the same shape as `session.managedSettings.get`. - [JsonPropertyName("resolved")] - public ManagedSettingsResolvedData Resolved { get => field ??= new(); set; } + /// Agent Finder materialisation revision identifier, a positive decimal integer. This is not a Git commit. + [JsonPropertyName("revisionId")] + public string RevisionId { get; set; } = string.Empty; - /// Typed effective values, as in `managedSettings.resolve`. - [JsonPropertyName("values")] - public ManagedSettingsValues? Values { get; set; } + /// Materialisation descriptor schema version, currently `1.0.0`. + [JsonPropertyName("schemaVersion")] + public string SchemaVersion { get; set; } = string.Empty; } -/// One candidate channel; absent settings represents a channel that delivered no document. +/// Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedSettingsComposeLayer +internal sealed class SkillPlanInstallFromReferenceRequest { - /// Candidate managed-settings document. Omit when the channel delivered none, as in resolve output. - [JsonPropertyName("settings")] - public JsonElement? Settings { get; set; } + /// Required authenticated bound catalogue, skill-confirmed-installation and skill-reference-installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// The channel whose candidate document is being supplied. - [JsonPropertyName("source")] - public ManagedSettingsChannel Source { get; set; } -} + /// Existing local session attached to this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; -/// Candidate managed-settings documents to merge without applying them. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ManagedSettingsComposeRequest -{ - /// 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; } + /// Materialisation reference to verify and plan. + [JsonPropertyName("reference")] + public CatalogMaterializationReference Reference { get => field ??= new(); set; } } -/// Polymorphic base type discriminated by kind. +/// 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(ManagedPermissionOperationUrl), "url")] -public partial class ManagedPermissionOperation +[JsonDerivedType(typeof(SkillInstallationResultOutcome), "outcome")] +[JsonDerivedType(typeof(SkillInstallationResultNegotiationRefused), "negotiation-refused")] +[JsonDerivedType(typeof(SkillInstallationResultInvalidRequest), "invalid-request")] +public partial class SkillInstallationResult { /// The type discriminator. [JsonPropertyName("kind")] @@ -7960,4086 +7638,4014 @@ public partial class ManagedPermissionOperation } -/// The url variant of . +/// The outcome variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ManagedPermissionOperationUrl : ManagedPermissionOperation +public partial class SkillInstallationResultOutcome : SkillInstallationResult { /// [JsonIgnore] - public override string Kind => "url"; + public override string Kind => "outcome"; - /// Absolute HTTP(S) URL with a host. Invalid URLs reject the entire evaluation batch. - [JsonPropertyName("url")] - public required string Url { 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; } } -/// RPC data type for ManagedPermissionEvaluation operations. +/// 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 ManagedPermissionEvaluation +public partial class SkillInstallationResultNegotiationRefused : SkillInstallationResult { - /// Original input operation associated with this verdict. - [JsonPropertyName("operation")] - public ManagedPermissionOperation Operation { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// Managed-policy decision only; never bypasses user permissions or other security controls. - [JsonPropertyName("verdict")] - public ManagedPermissionVerdict Verdict { 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; } + + /// 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; } } -/// Ordered managed permission verdicts and the supplied policy's fail-closed posture. +/// 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 sealed class ManagedSettingsPermissionsEvaluateResult +public partial class SkillInstallationResultInvalidRequest : SkillInstallationResult { - /// Managed permission policy could not be determined; every operation is denied. - [JsonPropertyName("failClosed")] - public bool FailClosed { get; set; } + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// One verdict per input operation, in the same order, including duplicates. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; set; } + /// 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; } } -/// Managed policy context and ordered operations for pure, sessionless evaluation. +/// Applies exactly one retained verified Skill installation plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ManagedSettingsPermissionsEvaluateRequest +internal sealed class SkillApplyInstallRequest { - /// Trusted, reusable managed permission policy snapshot; no policy is discovered during evaluation. - [JsonPropertyName("context")] - public ManagedPermissionsContext Context { get; set; } = null!; + /// Required authenticated bound catalogue and Skill installation capabilities. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Operations to evaluate, preserving input order and duplicates. An empty batch is valid. - [JsonPropertyName("operations")] - public IList Operations { get => field ??= []; set; } + /// 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; } -/// Indicates whether the calling client was registered as the session filesystem provider. +/// Read-only preparation of one owned Skill removal under fresh selected-session authority. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSetProviderResult +internal sealed class SkillPlanUninstallRequest { - /// Whether the provider was set successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } + + /// Exact receipt to inspect. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; + + /// Existing selected local session on this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; } -/// Optional capabilities declared by the provider. +/// One-use application of the exact retained Skill removal plan. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSetProviderCapabilities +internal sealed class SkillApplyUninstallRequest { - /// Whether the provider supports binary reads and writes through sessionFs.readFileBytes and sessionFs.writeFileBytes. - [JsonPropertyName("binary")] - public bool? Binary { get; set; } + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Whether the provider supports SQLite query/exists operations. - [JsonPropertyName("sqlite")] - public bool? Sqlite { 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; } -/// 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. +/// Server-side skill metadata, including name, description, source, enabled/invocable state, path, project path, and argument hint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionFsSetProviderRequest +public sealed class ServerSkill { - /// Optional capabilities declared by the provider. - [JsonPropertyName("capabilities")] - public SessionFsSetProviderCapabilities? Capabilities { get; set; } + /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. + [JsonPropertyName("argumentHint")] + public string? ArgumentHint { get; set; } - /// Path conventions used by this filesystem. - [JsonPropertyName("conventions")] - public SessionFsSetProviderConventions Conventions { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { 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; + /// Description of what the skill does. + [JsonPropertyName("description")] + public string Description { 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; + /// Whether the skill is currently enabled (based on global config). + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Unique identifier for the skill. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// 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; } } -/// Indicates whether the calling client was registered as the LLM inference provider. +/// Skills discovered across global and project sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceSetProviderResult +public sealed class ServerSkillList { - /// Whether the provider was set successfully. - [JsonPropertyName("success")] - public bool Success { get; 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; } } -/// Whether the start frame was accepted. +/// Optional project paths and additional skill directories to include in discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpResponseStartResult +internal sealed class SkillsDiscoverRequest { - /// True when the response start was matched to a pending request; false when unknown. - [JsonPropertyName("accepted")] - public bool Accepted { 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; } + + /// Optional skill scan paths to exclude from discovery. + [JsonPropertyName("ignoredSkillsLocations")] + public IList? IgnoredSkillsLocations { get; set; } + + /// Optional list of project directory paths to scan for project-scoped skills. + [JsonPropertyName("projectPaths")] + public IList? ProjectPaths { get; 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; } } -/// Response head. +/// 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 LlmInferenceHttpResponseStartRequest +public sealed class SkillDiscoveryPath { - /// HTTP response headers, preserving multiple values per name. - [JsonPropertyName("headers")] - public IDictionary> Headers { get => field ??= new Dictionary>(); set; } + /// Absolute path of the create/discovery target (may not exist on disk yet). + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Matches the requestId from the originating httpRequestStart frame. - [JsonPropertyName("requestId")] - public string RequestId { 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; } - /// HTTP status code. - [JsonPropertyName("status")] - public long Status { get; set; } + /// The input project path this directory was derived from (only for project scope). + [JsonPropertyName("projectPath")] + public string? ProjectPath { get; set; } - /// Optional HTTP status reason phrase. - [JsonPropertyName("statusText")] - public string? StatusText { get; set; } + /// Which tier this directory belongs to. + [JsonPropertyName("scope")] + public SkillDiscoveryScope Scope { get; set; } } -/// Whether the chunk was accepted. +/// Canonical locations where skills can be created so the runtime will recognize them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpResponseChunkResult +public sealed class SkillDiscoveryPathList { - /// True when the chunk was matched to a pending request; false when unknown. - [JsonPropertyName("accepted")] - public bool Accepted { get; set; } + /// Canonical skill create/discovery directories, in priority order. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } } -/// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. +/// Optional project paths to enumerate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpResponseChunkError +public sealed class SkillsGetDiscoveryPathsRequest { - /// Optional machine-readable error code. - [JsonPropertyName("code")] - public string? Code { get; set; } + /// 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; } - /// Human-readable failure description. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// 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 response body chunk or terminal error. +/// Inventory request under an explicitly selected existing session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LlmInferenceHttpResponseChunkRequest +internal sealed class SkillInstallationsRequest { - /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. - [JsonPropertyName("binary")] - public bool? Binary { 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; + /// Required authenticated bound installation contract. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); 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; } + /// Existing selected local session on this connection. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; +} - /// 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; } +/// Existing-operation control. A new session selector is deliberately not accepted. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SkillInstallationOperationRequest +{ + /// Required authenticated bound Skill installation capability. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Matches the requestId from the originating httpRequestStart frame. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Exact runtime-issued operation ID on the original connection. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; } -/// Pre-resolved working-directory context for session startup. +/// Persisted enablement update for one owned Skill installation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionContext +internal sealed class SkillSetEnabledRequest { - /// Active git branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Required authenticated bound Skill installation capability. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); set; } - /// Most recent working directory for this session. - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; + /// Persisted enablement value. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Git repository root, if the cwd was inside a git repo. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// Exact receipt identity to update. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; - /// Repository host type. - [JsonPropertyName("hostType")] - public SessionContextHostType? HostType { get; set; } + /// Existing selected local session to reconcile after persistence. + [JsonPropertyName("policySessionId")] + public string PolicySessionId { get; set; } = string.Empty; +} - /// Repository slug in `owner/name` form, when known. - [JsonPropertyName("repository")] - public string? Repository { get; set; } +/// Skill names to mark as disabled in global configuration, replacing any previous list. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SkillsConfigSetDisabledSkillsRequest +{ + /// List of skill names to disable. + [JsonPropertyName("disabledSkills")] + public IList DisabledSkills { get => field ??= []; set; } } -/// GitHub repository the remote session belongs to. +/// Adds or removes a single skill from the global disabled list, leaving every other entry untouched. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteSessionMetadataRepository +internal sealed class SkillsConfigSetSkillDisabledRequest { - /// Branch associated with the remote session. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; + /// True to disable the skill, false to enable it. + [JsonPropertyName("disabled")] + public bool Disabled { get; set; } - /// Repository name. + /// Name of the skill to add to or remove from the disabled list. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - - /// Repository owner. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; } -/// Remote session metadata for the session to hand off (typically obtained from `sessions.list` with `source: "remote"`). +/// 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)] -public sealed class RemoteSessionMetadataValue +[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 { - /// 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; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } +/// 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)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogCandidateMcpServer), "mcp-server")] +[JsonDerivedType(typeof(CatalogCandidateAiSkill), "ai-skill")] +[JsonDerivedType(typeof(CatalogCandidatePlugin), "plugin")] +public partial class CatalogCandidate +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } +/// 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)] +public sealed class CatalogMcpServerCandidateProvenance +{ + /// 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; - /// Pull request number associated with the session. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } + /// JSON MCP media type advertised for the referenced card. + [JsonPropertyName("mediaType")] + public McpServerCardMediaType MediaType { get; set; } - /// Backing remote session IDs (most recent first). - [JsonPropertyName("remoteSessionIds")] - public IList RemoteSessionIds { get => field ??= []; 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; +} - /// GitHub repository the remote session belongs to. - [JsonPropertyName("repository")] - public RemoteSessionMetadataRepository Repository { get => field ??= new(); 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. +/// 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 +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; +/// 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)] +public partial class CatalogCandidateSourceUrl : CatalogCandidateSource +{ + /// + [JsonIgnore] + public override string Kind => "url"; - /// Deadline (ISO 8601) at which a CLI remote session becomes stale without further heartbeats. - [JsonPropertyName("staleAt")] - public string? StaleAt { get; set; } + /// 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; } +} - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("startTime")] - public string StartTime { get; set; } = string.Empty; +/// 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 partial class CatalogCandidateSourceEmbedded : CatalogCandidateSource +{ + /// + [JsonIgnore] + public override string Kind => "embedded"; +} - /// Server-side task state returned by GitHub. - [JsonPropertyName("state")] - public string? State { 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 . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogCandidateMcpServer : CatalogCandidate +{ + /// + [JsonIgnore] + public override string Kind => "mcp-server"; - /// Short summary of the session, when one has been derived. - [JsonPropertyName("summary")] - public string? Summary { 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 the remote task originated from CCA or CLI `--remote`. - [JsonPropertyName("taskType")] - public RemoteSessionMetadataTaskType? TaskType { 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 MCP server can be planned for installation, and if policy prevents it. + [JsonPropertyName("installability")] + public required CatalogMcpServerInstallability Installability { get; set; } + + /// JSON MCP media type of the underlying card. + [JsonPropertyName("mediaType")] + public required McpServerCardMediaType MediaType { get; set; } + + /// 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 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; } } -/// `sessions.open` handoff progress update with step, status, and optional message. +/// 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 SessionsOpenProgress +public sealed class CatalogAiSkillCandidateProvenance { - /// Optional step message. - [JsonPropertyName("message")] - public string? Message { 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; - /// Step status. - [JsonPropertyName("status")] - public SessionsOpenProgressStatus Status { get; set; } + /// Media type advertised for the referenced AI skill card. + [JsonPropertyName("mediaType")] + public CatalogAiSkillMediaType MediaType { get; set; } - /// Handoff step. - [JsonPropertyName("step")] - public SessionsOpenProgressStep Step { 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; } -/// Result of opening a session. +/// 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 SessionOpenResult +public partial class CatalogCandidateAiSkill : CatalogCandidate { - /// Remote session metadata, present when status is `connected`. - [JsonPropertyName("metadata")] - public RemoteSessionMetadataValue? Metadata { get; set; } + /// + [JsonIgnore] + public override string Kind => "ai-skill"; - /// Handoff progress steps, present when status is `handed_off`. - [JsonPropertyName("progress")] - public IList? Progress { 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; } - /// Remote session ID, present when status is `connected`. - [JsonPropertyName("remoteSessionId")] - public string? RemoteSessionId { 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; } - /// Opened session ID. Omitted when status is `not_found`. - [JsonPropertyName("sessionId")] - public string? SessionId { 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; } - /// 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; } + /// ISO 8601 timestamp after which the handle is stale and will be rejected. + [JsonPropertyName("handleExpiresAt")] + public required string HandleExpiresAt { get; set; } - /// Outcome of the open request. - [JsonPropertyName("status")] - public SessionsOpenStatus Status { 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; } -/// Identifier and optional friendly name assigned to the newly forked session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsForkResult -{ - /// Friendly name assigned to the forked session, if any. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Media type of the underlying AI skill card. + [JsonPropertyName("mediaType")] + public required CatalogAiSkillMediaType MediaType { get; set; } - /// The new forked session's ID. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. +/// One place an Agent Plugin can install from: Agent Finder's materialisation, a marketplace, or a GitHub repository. Each variant carries only its own fields. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsForkRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CatalogAgentPluginInstallSourceMaterialization), "materialization")] +[JsonDerivedType(typeof(CatalogAgentPluginInstallSourceMarketplace), "marketplace")] +[JsonDerivedType(typeof(CatalogAgentPluginInstallSourceRepository), "repository")] +public partial class CatalogAgentPluginInstallSource { - /// Optional friendly name to assign to the forked session. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } +/// Agent Finder's verified materialisation of the plugin. Present only when the candidate carries a complete materialisation reference; the reference itself stays runtime-private. +/// The materialization variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class CatalogAgentPluginInstallSourceMaterialization : CatalogAgentPluginInstallSource +{ + /// + [JsonIgnore] + public override string Kind => "materialization"; } -/// Repository associated with the connected remote session. +/// A plugin marketplace named by verified catalogue install data, installed as `pluginName@name`. +/// The marketplace variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectedRemoteSessionMetadataRepository +public partial class CatalogAgentPluginInstallSourceMarketplace : CatalogAgentPluginInstallSource { - /// Branch associated with the remote session. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "marketplace"; - /// Repository name. + /// Marketplace name. + [RegularExpression("^[A-Za-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(1)] + [MaxLength(64)] [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public required string Name { get; set; } - /// Repository owner or organization login. - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Canonical lowercase GitHub owner/repository hosting the marketplace. + [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 required string Repository { get; set; } } -/// Metadata for a connected remote session. +/// A GitHub repository holding the plugin, installed directly as `owner/repo[:path]` at the plan's commit, without a marketplace. +/// The repository variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectedRemoteSessionMetadata +public partial class CatalogAgentPluginInstallSourceRepository : CatalogAgentPluginInstallSource { - /// Neutral SDK discriminator for the connected remote session kind. - [JsonPropertyName("kind")] - public ConnectedRemoteSessionMetadataKind Kind { get; set; } + /// + [JsonIgnore] + public override string Kind => "repository"; - /// Last session update time as an ISO 8601 string. - [JsonPropertyName("modifiedTime")] - public DateTimeOffset ModifiedTime { get; set; } + /// Relative POSIX path of the plugin directory in the repository. Omitted when the plugin is at 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)] + [MaxLength(256)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] + public string? Path { get; set; } - /// Optional friendly session name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Canonical lowercase GitHub owner/repository holding the plugin. + [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 required string Repository { get; set; } +} - /// Pull request number associated with the session. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } +/// What installing an Agent Plugin would do, for the user to review and confirm. Provisional: the catalogue install data it is built from is not final. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CatalogAgentPluginInstallPlan +{ + /// Canvas IDs the plugin contributes. Non-empty exactly when components include `canvas`. + [JsonPropertyName("canvasIds")] + public IList CanvasIds { get => field ??= []; set; } - /// Repository associated with the connected remote session. - [JsonPropertyName("repository")] - public ConnectedRemoteSessionMetadataRepository Repository { get => field ??= new(); set; } + /// Exact full commit ID to install, in lowercase hex. Always present when the plan has a repository source; a plan with a marketplace source carries at least one of version and commit. + [RegularExpression("^(?:[0-9a-f]{40}|[0-9a-f]{64})$")] + [JsonPropertyName("commit")] + public string? Commit { get; set; } - /// Original remote resource identifier. - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } + /// Components the plugin contributes, in canonical order. + [JsonPropertyName("components")] + public IList Components { get => field ??= []; set; } - /// SDK session ID for the connected remote session. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Connectors the plugin depends on. + [JsonPropertyName("connectorDependencies")] + public IList ConnectorDependencies { get => field ??= []; set; } - /// Remote session staleness deadline as an ISO 8601 string. - [JsonPropertyName("staleAt")] - public DateTimeOffset? StaleAt { get; set; } + /// Extension IDs the plugin contributes. Non-empty exactly when components include `extension`. + [JsonPropertyName("extensionIds")] + public IList ExtensionIds { get => field ??= []; set; } - /// Session start time as an ISO 8601 string. - [JsonPropertyName("startTime")] - public DateTimeOffset StartTime { get; set; } + /// MCP servers the plugin depends on but does not bundle. + [JsonPropertyName("mcpDependencies")] + public IList McpDependencies { get => field ??= []; set; } - /// Remote session state returned by the backing service. - [JsonPropertyName("state")] - public string? State { get; set; } + /// The plugin's name, as named in its marketplace or manifest. + [RegularExpression("^[A-Za-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(1)] + [MaxLength(64)] + [JsonPropertyName("pluginName")] + public string PluginName { get; set; } = string.Empty; - /// Optional session summary. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + /// Every valid install source, in preference order: materialization, then marketplace, then repository. Each kind appears at most once. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } + + /// Exact plugin version to install. A plan with a marketplace source carries at least one of version and commit. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Remote session connection result. +/// 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 RemoteSessionConnectionResult +public sealed class CatalogAgentPluginCandidateProvenance { - /// Metadata for a connected remote session. - [JsonPropertyName("metadata")] - public ConnectedRemoteSessionMetadata Metadata { get => field ??= new(); 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; - /// SDK session ID for the connected remote session. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Media type naming the plugin's format. + [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; } -/// Remote session connection parameters. +/// 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)] -internal sealed class ConnectRemoteSessionParams +public sealed class CatalogPluginRepositorySource { - /// Session ID to connect to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } -/// Local or remote session metadata entry. Narrow on `isRemote` to access source-specific fields. -/// Data type discriminated by isRemote. +/// 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 partial class SessionListEntry +public partial class CatalogCandidatePlugin : CatalogCandidate { - /// The boolean discriminator. - [JsonPropertyName("isRemote")] - public bool IsRemote { get; set; } + /// + [JsonIgnore] + public override string Kind => "plugin"; - /// Runtime client name that created/last resumed this session. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("clientName")] - public string? ClientName { get; 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; } - /// Pre-resolved working-directory context for session startup. + /// 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("context")] - public SessionContext? Context { get; set; } + [JsonPropertyName("description")] + public string? Description { 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; } + /// 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; } - /// 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; } + /// 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; } - /// True for detached maintenance sessions that should be hidden from normal resume lists. + /// Whether this plugin can be installed in the selected session. Present only when the caller requires the agent-plugin-confirmed-installation capability. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("isDetached")] - public bool? IsDetached { get; set; } + [JsonPropertyName("installability")] + public CatalogAgentPluginInstallability? Installability { get; set; } - /// GitHub task ID, when this local session is bound to one. Only present for local sessions exported to remote control. + /// Reviewable install plan built only from verified catalogue install data. Present exactly when installability is `installable`; the runtime never derives it from the repository source, display text, or identity. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mcTaskId")] - public string? McTaskId { get; set; } + [JsonPropertyName("installPlan")] + public CatalogAgentPluginInstallPlan? InstallPlan { get; set; } - /// Last-modified time of the session's persisted state, as ISO 8601. - [JsonPropertyName("modifiedTime")] - public required string ModifiedTime { get; set; } + /// Media type naming the plugin's format. + [JsonPropertyName("mediaType")] + public required CatalogAgentPluginMediaType MediaType { get; set; } - /// Optional human-friendly name set via /rename. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("name")] - public string? Name { 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; } - /// Pull request number associated with the session. + /// 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("pullRequestNumber")] - public long? PullRequestNumber { get; set; } + [JsonPropertyName("publisher")] + public string? Publisher { get; set; } - /// Backing remote session IDs (most recent first). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("remoteSessionIds")] - public IList? RemoteSessionIds { 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; } - /// GitHub repository the remote session belongs to. + /// 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("repository")] - public RemoteSessionMetadataRepository? Repository { get; set; } + [JsonPropertyName("trust")] + public CatalogTrustSnapshot? Trust { get; set; } - /// Original remote resource identifier (task ID or PR node ID). + /// Optional version declared by the catalog source. Omitted rather than guessed when the source supplies no version. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("resourceId")] - public string? ResourceId { get; set; } + [JsonPropertyName("version")] + public string? Version { get; set; } +} - /// Stable session identifier. - [JsonPropertyName("sessionId")] - public required string SessionId { 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CatalogSearchPagination +{ + /// One-based page returned by the authority. + [JsonPropertyName("currentPage")] + public int CurrentPage { 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; } + /// 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; } - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("startTime")] - public required string StartTime { 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; } - /// Server-side task state returned by GitHub. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("state")] - public string? State { 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; } - /// Short summary of the session, when one has been derived. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("summary")] - public string? Summary { get; set; } + /// Page size bound to the search, equal to the effective request limit. + [JsonPropertyName("pageSize")] + public int PageSize { get; set; } - /// Whether the remote task originated from CCA or CLI `--remote`. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("taskType")] - public RemoteSessionMetadataTaskType? TaskType { get; set; } -} - -/// Sessions matching the filter, ordered most-recently-modified first. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionList -{ - /// Sessions ordered most-recently-modified first. Discriminated by `isRemote`. - [JsonPropertyName("sessions")] - public IList Sessions { get => field ??= []; set; } -} - -/// Optional filter applied to the returned sessions. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionListFilter -{ - /// Match sessions whose context.branch equals this value. - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// Match sessions whose context.cwd equals this value. - [JsonPropertyName("cwd")] - public string? Cwd { 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; - /// Match sessions whose context.gitRoot equals this value. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// 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; } - /// Match sessions whose context.repository equals this value. - [JsonPropertyName("repository")] - public string? Repository { 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; } } -/// Optional source filter, metadata-load limit, and context filter applied to the returned sessions. +/// 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 SessionsListRequest +public partial class CatalogSearchResultSucceeded : CatalogSearchResult { - /// Optional filter applied to the returned sessions. - [JsonPropertyName("filter")] - public SessionListFilter? Filter { get; set; } + /// + [JsonIgnore] + public override string Kind => "succeeded"; - /// When true, include detached maintenance sessions. Defaults to false for user-facing session lists. - [JsonPropertyName("includeDetached")] - public bool? IncludeDetached { get; set; } + /// Matching candidates, never more than the requested limit. All text is inert untrusted data. + [JsonPropertyName("candidates")] + public required IList Candidates { 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; } + /// Protocol version and capabilities the runtime honoured. + [JsonPropertyName("negotiated")] + public required CatalogNegotiatedContract Negotiated { get; set; } - /// Which session sources to include. Defaults to `local` for backward compatibility. - [JsonPropertyName("source")] - public SessionSource? Source { 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; } - /// 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; } + /// 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; } } -/// Persisted local session metadata, including identifiers, timestamps, summary/name, client, context, detached state, and task ID. +/// 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 LocalSessionMetadataValue +public partial class CatalogSearchResultNegotiationRefused : CatalogSearchResult { - /// Runtime client name that created/last resumed this session. - [JsonPropertyName("clientName")] - public string? ClientName { 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; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// 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; } + /// 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; } - /// Last-modified time of the session's persisted state, as ISO 8601. - [JsonPropertyName("modifiedTime")] - public string ModifiedTime { get; set; } = string.Empty; + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } - /// Optional human-friendly name set via /rename. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } - /// Stable session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Protocol version of the runtime that refused the request. + [JsonPropertyName("runtimeProtocolVersion")] + public required long RuntimeProtocolVersion { get; set; } - /// Session creation time as an ISO 8601 timestamp. - [JsonPropertyName("startTime")] - public string StartTime { 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; } - /// Short summary of the session, when one has been derived. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + /// The subset of the caller's bounded extensible capability identifiers this runtime cannot honour. + [JsonPropertyName("unsupportedCapabilities")] + public required IList UnsupportedCapabilities { get; set; } } -/// Persisted local session metadata when the session exists. +/// The request asked for a candidate kind this runtime does not serve. +/// The unsupported-kind variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetMetadataResult +public partial class CatalogSearchResultUnsupportedKind : CatalogSearchResult { - /// Local session metadata, omitted when the session does not exist. - [JsonPropertyName("session")] - public LocalSessionMetadataValue? Session { get; set; } -} + /// + [JsonIgnore] + public override string Kind => "unsupported-kind"; -/// 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; -} + /// 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; } -/// Client metadata outcome for one requested local session. -/// Polymorphic base type discriminated by status. -[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 -{ - /// The type discriminator. - [JsonPropertyName("status")] - public virtual string Status { get; set; } = string.Empty; -} + /// 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 ok 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 SessionsClientMetadataEntryOk : SessionsClientMetadataEntry +public partial class CatalogSearchResultInvalidRequest : CatalogSearchResult { /// [JsonIgnore] - public override string Status => "ok"; + public override string Kind => "invalid-request"; - /// Validated client metadata, possibly empty or projected to requested keys. - [JsonPropertyName("metadata")] - public required IDictionary Metadata { get; set; } + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { 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 notFound 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 SessionsClientMetadataEntryNotFound : SessionsClientMetadataEntry +public partial class CatalogSearchResultAuthenticationRequired : CatalogSearchResult { /// [JsonIgnore] - public override string Status => "notFound"; + public override string Kind => "authentication-required"; - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { 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; } + + /// 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; } } -/// The corrupt variant of . +/// Registry or enterprise policy refused the operation. +/// The policy-rejected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionsClientMetadataEntryCorrupt : SessionsClientMetadataEntry +public partial class CatalogSearchResultPolicyRejected : CatalogSearchResult { /// [JsonIgnore] - public override string Status => "corrupt"; + public override string Kind => "policy-rejected"; - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { 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; } } -/// The unsupportedVersion 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 SessionsClientMetadataEntryUnsupportedVersion : SessionsClientMetadataEntry +public partial class CatalogSearchResultNetworkFailure : CatalogSearchResult { /// [JsonIgnore] - public override string Status => "unsupportedVersion"; + public override string Kind => "network-failure"; - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { 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; } + + /// 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; } } -/// The unavailable 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 SessionsClientMetadataEntryUnavailable : SessionsClientMetadataEntry +public partial class CatalogSearchResultUnsafeRetrieval : CatalogSearchResult { /// [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; } + public override string Kind => "unsafe-retrieval"; - /// Human-readable diagnostic message. Not stable for programmatic matching. + /// 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; } - /// Requested session ID. - [JsonPropertyName("sessionId")] - public required string SessionId { 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; } } -/// Bounded batch request for client-owned metadata from persisted local sessions. +/// 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)] -internal sealed class SessionsGetClientMetadataRequest +public partial class CatalogSearchResultMalformedCard : CatalogSearchResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "malformed-card"; - /// Session IDs to inspect. Results preserve this order. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; 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; } + + /// 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; } } -/// Batch of session events returned by a read, with cursor and continuation metadata. +/// 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 sealed class EventsReadResult +public partial class CatalogSearchResultContractViolation : CatalogSearchResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "contract-violation"; - /// 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; } + /// 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; } - /// 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; } + /// Which rule the response broke. + [JsonPropertyName("reason")] + public required CatalogContractViolationReason Reason { get; set; } } -/// Pagination options for reading an inactive or active local session's persisted event journal. +/// 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)] -internal sealed class SessionsReadPersistedEventsRequest +public partial class CatalogSearchResultUnavailable : CatalogSearchResult { - /// 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; } - - /// 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; } + /// + [JsonIgnore] + public override string Kind => "unavailable"; - /// 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; } + /// 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; } - /// Session ID whose persisted event journal should be read. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Why the operation is unavailable. + [JsonPropertyName("reason")] + public required CatalogUnavailableReason Reason { get; set; } } -/// Recent local session IDs that contain user-visible history. +/// 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)] -internal sealed class SessionsListNonEmptySessionIdsResult +public sealed class CatalogSearchPage { - /// Session IDs ordered newest-first. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; 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; } + + /// 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; } -/// Limit for non-empty local session IDs. +/// 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 SessionsListNonEmptySessionIdsRequest +public sealed class CatalogSearchRequest { - /// Maximum number of session IDs to return. + /// Protocol version and capabilities the caller requires. + [JsonPropertyName("contract")] + public required CatalogClientContract Contract { get; 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; } + + /// Maximum number of candidates to return. Defaults to 10 when omitted. [JsonPropertyName("limit")] - public long? Limit { get; set; } -} + public int? Limit { get; set; } -/// ID of the local session bound to the given GitHub task, or omitted when none. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsFindByTaskIDResult -{ - /// Omitted when no local session is bound to that GitHub task. - [JsonPropertyName("sessionId")] - public string? SessionId { 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; } -/// 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; -} + /// + /// 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; } -/// 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; } + /// 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; } } -/// UUID prefix to resolve to a unique session ID. +/// 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)] -internal sealed class SessionsFindByPrefixRequest +[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 { - /// 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; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// 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; } -} -/// Optional working-directory context used to score session relevance. +/// 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)] -internal sealed class SessionsGetLastForContextRequest +public partial class CatalogSelectionResultSelected : CatalogSelectionResult { - /// Optional working-directory context used to score session relevance. When omitted the most-recently-modified session wins. - [JsonPropertyName("context")] - public SessionContext? Context { get; set; } -} + /// + [JsonIgnore] + public override string Kind => "selected"; -/// Absolute path to the session's events.jsonl file on disk. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetEventFilePathResult -{ - /// Absolute path to the session's events.jsonl file. - [JsonPropertyName("filePath")] - public string FilePath { get; set; } = string.Empty; -} + /// 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; } -/// Session ID whose event-log file path to compute. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetEventFilePathRequest -{ - /// Session ID whose event-log file path to compute. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Map of sessionId -> on-disk size in bytes for each session's workspace directory. +/// 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 SessionSizes +public partial class CatalogSelectionResultDeclined : CatalogSelectionResult { - /// Map of sessionId -> on-disk size in bytes for the session's workspace directory. - [JsonPropertyName("sizes")] - public IDictionary Sizes { get => field ??= new Dictionary(); set; } -} + /// + [JsonIgnore] + public override string Kind => "declined"; -/// Session IDs from the input set that are currently in use by another process. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsCheckInUseResult -{ - /// 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; } + /// 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; } } -/// Session IDs to test for live in-use locks. +/// The caller cancelled the selection interaction and the retained search state was released. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCheckInUseRequest +public partial class CatalogSelectionResultCancelled : CatalogSelectionResult { - /// Session IDs to test for live in-use locks. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; set; } -} + /// + [JsonIgnore] + public override string Kind => "cancelled"; -/// 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; } + /// 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; } } -/// Session ID to look up the persisted remote-steerable flag for. +/// 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)] -internal sealed class SessionsGetPersistedRemoteSteerableRequest +public partial class CatalogSelectionResultTimedOut : CatalogSelectionResult { - /// Session ID to look up the persisted remote-steerable flag for. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "timed-out"; + + /// 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; } } -/// 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. +/// The selection reference was malformed or unknown. +/// The invalid variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsCloseResult +public partial class CatalogSelectionResultInvalid : CatalogSelectionResult { + /// + [JsonIgnore] + public override string Kind => "invalid"; + + /// 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; } } -/// Session ID to close. +/// The runtime-enforced selection reference lifetime elapsed before the request arrived. +/// The stale variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCloseRequest +public partial class CatalogSelectionResultStale : CatalogSelectionResult { - /// Session ID to close. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "stale"; + + /// 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; } } -/// Map of sessionId -> bytes freed by removing the session's workspace directory. +/// 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 SessionBulkDeleteResult +public partial class CatalogSelectionResultReplayed : CatalogSelectionResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "replayed"; + + /// 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; } } -/// Session IDs to close, deactivate, and delete from disk. +/// The selection reference belongs to another runtime instance or session. +/// The foreign variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsBulkDeleteRequest +public partial class CatalogSelectionResultForeign : CatalogSelectionResult { - /// Session IDs to close, deactivate, and delete from disk. - [JsonPropertyName("sessionIds")] - public IList SessionIds { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "foreign"; + + /// 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; } } -/// Session ID to delete from disk. +/// The presented opaque handle was issued for another catalog operation. +/// The wrong-kind variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsDeleteRequest +public partial class CatalogSelectionResultWrongKind : CatalogSelectionResult { - /// Session ID to delete. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "wrong-kind"; - /// Internal resolved session directory path to delete. - [JsonPropertyName("sessionPath")] - public string? SessionPath { 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; } } -/// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. +/// 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 SessionPruneResult +public partial class CatalogSelectionResultNegotiationRefused : CatalogSelectionResult { - /// Session IDs that would be deleted in dry-run mode (always empty otherwise). - [JsonPropertyName("candidates")] - public IList Candidates { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "negotiation-refused"; - /// Session IDs that were deleted (always empty in dry-run mode). - [JsonPropertyName("deleted")] - public IList Deleted { 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; } - /// True when no deletions were actually performed. - [JsonPropertyName("dryRun")] - public bool DryRun { get; set; } + /// Lowest caller protocol version this runtime will serve. + [JsonPropertyName("minimumSupportedProtocolVersion")] + public required long MinimumSupportedProtocolVersion { get; set; } - /// Total bytes freed (actual when not dry-run, projected when dry-run). - [JsonPropertyName("freedBytes")] - public long FreedBytes { get; set; } + /// Whether the version or the capability set was the problem. + [JsonPropertyName("reason")] + public required CatalogNegotiationRefusedReason Reason { get; set; } - /// Session IDs that were skipped (e.g., named sessions). - [JsonPropertyName("skipped")] - public IList Skipped { get => field ??= []; 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; } } -/// Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). +/// 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 SessionsPruneOldRequest +public partial class CatalogSelectionResultInvalidRequest : CatalogSelectionResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "invalid-request"; - /// When true, named sessions (set via /rename) are also eligible for pruning. - [JsonPropertyName("includeNamed")] - public bool? IncludeNamed { get; set; } + /// Which request field was rejected. + [JsonPropertyName("field")] + public required CatalogInvalidRequestField Field { get; set; } - /// Delete sessions whose modifiedTime is at least this many days old. - [JsonPropertyName("olderThanDays")] - public long OlderThanDays { 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; } } -/// Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed). +/// 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 SessionsSaveResult +public partial class CatalogSelectionResultUnavailable : CatalogSelectionResult { -} + /// + [JsonIgnore] + public override string Kind => "unavailable"; -/// 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; -} + /// 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; } -/// 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 SessionsReleaseLockResult -{ + /// Why the operation is unavailable. + [JsonPropertyName("reason")] + public required CatalogUnavailableReason Reason { get; set; } } -/// Session ID whose in-use lock should be released. +/// 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 SessionsReleaseLockRequest +internal sealed class CatalogSelectionRequest { - /// Session ID whose in-use lock should be released. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Protocol version and capabilities the caller requires. + [JsonPropertyName("contract")] + public CatalogClientContract Contract { get => field ??= new(); 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; } -} + /// 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; } -/// 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; } -} + /// 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; -/// 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; + /// 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; } -/// A working-directory context together with the client that produced it. +/// Information about an installed plugin tracked in global state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionWorkingDirectoryContextWithClient +public sealed class InstalledPluginInfo { - /// Merge-base commit SHA. - [JsonPropertyName("baseCommit")] - public string? BaseCommit { 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; } - /// Current git branch name. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Whether the plugin is currently enabled for new sessions. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Name of the client that created the session. - [JsonPropertyName("clientName")] - public string? ClientName { 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; } - /// 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 — 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; } - /// Root directory of the git repository. - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// Whether enterprise managed settings control this plugin's enabled state. + [JsonPropertyName("managed")] + public bool? Managed { get; set; } - /// Head commit of the current git branch. - [JsonPropertyName("headCommit")] - public string? HeadCommit { get; set; } + /// The enabled state required by enterprise managed settings, when this plugin spec is managed. + [JsonPropertyName("managedDesiredEnabled")] + public bool? ManagedDesiredEnabled { get; set; } - /// Hosting platform type of the repository. - [JsonPropertyName("hostType")] - public string? HostType { get; set; } + /// Marketplace the plugin came from. Empty string ("") for direct repo / URL / local installs. + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; - /// Repository identifier derived from the git remote URL. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// Plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Raw host string from the git remote URL. - [JsonPropertyName("repositoryHost")] - public string? RepositoryHost { 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; } + + /// Installed version (when reported by the plugin manifest). + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Identity, state location and starting context for a workspace record. +/// Plugins installed in user/global state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsCreateWorkspaceRequest +public sealed class PluginListResult { - /// 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; } + /// Installed plugins. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; 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; +/// Result of installing a plugin. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PluginInstallResult +{ + /// 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; } - /// User-supplied display name for the workspace. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// The newly installed plugin's metadata. + [JsonPropertyName("plugin")] + public InstalledPluginInfo Plugin { get => field ??= new(); set; } - /// Session ID the workspace record belongs to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional post-install message provided by the plugin (e.g. setup instructions). + [JsonPropertyName("postInstallMessage")] + public string? PostInstallMessage { 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; -} + /// Number of skills discovered and installed from the plugin. + [JsonPropertyName("skillsInstalled")] + public long SkillsInstalled { get; set; } -/// The workspace record on disk, omitted when the session has none. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsLoadWorkspaceResult -{ - /// The workspace record, as JSON. Omitted when the record does not exist. - [JsonPropertyName("workspaceJson")] - public string? WorkspaceJson { get; set; } + /// Where the completed plugin tree was staged before atomic promotion. + [JsonPropertyName("stagingMode")] + public PluginInstallStagingMode? StagingMode { get; set; } } -/// Where the session's state lives, as a root directory and the session ID under it. +/// Plugin source and optional working directory for relative-path resolution. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsLoadWorkspaceRequest +internal sealed class PluginsInstallRequest { - /// 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; + /// 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; - /// Root directory every session's state directory sits under. - [JsonPropertyName("sessionsHome")] - public string SessionsHome { 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; } } -/// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. +/// Name (or spec) of the plugin to uninstall. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsUpdateWorkspaceFieldsResult +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; } + + /// Plugin name or "plugin@marketplace" spec to uninstall. When ambiguous, prefer the fully-qualified spec. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// 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. +/// Result of updating a single plugin. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsUpdateWorkspaceFieldsRequest +public sealed class PluginUpdateResult { - /// 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; + /// Version after the update, when reported by the plugin manifest. + [JsonPropertyName("newVersion")] + public string? NewVersion { 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; + /// Version that was previously installed, when available. + [JsonPropertyName("previousVersion")] + public string? PreviousVersion { get; set; } - /// Root directory every session's state directory sits under. - [JsonPropertyName("sessionsHome")] - public string SessionsHome { get; set; } = string.Empty; + /// Number of skills discovered and installed after the update. + [JsonPropertyName("skillsInstalled")] + public long SkillsInstalled { 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. +/// Name (or spec) of the plugin to update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionsReloadPluginHooksResult +internal sealed class PluginsUpdateRequest { + /// Plugin name or "plugin@marketplace" spec to update. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Active session ID and an optional flag for deferring repo-level hooks until folder trust. +/// Per-plugin result from updating all plugins, with versions, skills installed, success flag, and optional error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsReloadPluginHooksRequest +public sealed class PluginUpdateAllEntry { - /// When true, skip repo-level hooks. Use before folder trust is confirmed; loadDeferredRepoHooks loads them post-trust. - [JsonPropertyName("deferRepoHooks")] - public bool? DeferRepoHooks { get; set; } + /// Error message (failure only). + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Active session ID to reload hooks for. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Marketplace the plugin came from. Empty string ("") for direct installs. + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; -/// Queued repo-level startup prompts and the total hook command count after loading. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLoadDeferredRepoHooksResult -{ - /// 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; } + /// Plugin name that was updated. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// 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; } -} + /// Version after the update, when available. + [JsonPropertyName("newVersion")] + public string? NewVersion { get; set; } -/// Active session ID whose deferred repo-level hooks should be loaded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsLoadDeferredRepoHooksRequest -{ - /// Active session ID whose deferred repo-level hooks should be loaded. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Previously installed version, when available. + [JsonPropertyName("previousVersion")] + public string? PreviousVersion { 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 -{ -} + /// Number of skills installed after the update (success only). + [JsonPropertyName("skillsInstalled")] + public long? SkillsInstalled { 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; } + /// Whether the update succeeded for this plugin. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Dynamic-context board entry count, when available. +/// Result of updating all installed plugins. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetBoardEntryCountResult +public sealed class PluginUpdateAllResult { - /// Board entry count, when available. - [JsonPropertyName("count")] - public long? Count { get; set; } + /// Per-plugin update results in deterministic order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// Session ID whose board entry count should be returned. +/// Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsGetBoardEntryCountRequest +internal sealed class PluginsEnableRequest { - /// Session ID whose board entry count should be returned. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// 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; } -/// State of the runtime-managed remote-control singleton. -/// Polymorphic base type discriminated by state. -[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 -{ - /// The type discriminator. - [JsonPropertyName("state")] - public virtual string State { get; set; } = string.Empty; + /// 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; } } - -/// Remote control is not connected. -/// The off variant of . +/// Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusOff : RemoteControlStatus +internal sealed class PluginsDisableRequest { - /// - [JsonIgnore] - public override string State => "off"; + /// 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; } + + /// 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; } } -/// Remote control is in the middle of initial setup. -/// The connecting variant of . +/// Trusted built-in plugin directories to use for this runtime process. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusConnecting : RemoteControlStatus +internal sealed class PluginsBuiltinSetRequest { - /// - [JsonIgnore] - public override string State => "connecting"; - - /// Session id the connection is attaching to. - [JsonPropertyName("attachedSessionId")] - public required string AttachedSessionId { 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; } } -/// Remote control is connected to a local session. -/// The active variant of . +/// Registered marketplace summary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusActive : RemoteControlStatus +public sealed class MarketplaceInfo { - /// - [JsonIgnore] - public override string State => "active"; + /// 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; } - /// Session id remote control is pointed at. - [JsonPropertyName("attachedSessionId")] - public required string AttachedSessionId { get; set; } + /// True when this is a default marketplace shipped with the runtime. Defaults are not removable. + [JsonPropertyName("isDefault")] + public bool? IsDefault { 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; } + /// Whether enterprise managed settings provide and control this marketplace entry. + [JsonPropertyName("managed")] + public bool? Managed { get; set; } - /// MC frontend URL for this session, when known. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("frontendUrl")] - public string? FrontendUrl { get; set; } + /// Marketplace name (matches the @marketplace suffix in plugin specs). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Whether the MC session may steer this session. - [JsonPropertyName("isSteerable")] - public required bool IsSteerable { get; set; } + /// 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 last setup attempt failed. The singleton is otherwise off. -/// The error variant of . +/// All registered marketplaces, including built-in defaults. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class RemoteControlStatusError : RemoteControlStatus +public sealed class MarketplaceListResult { - /// - [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; } + /// Registered marketplaces. + [JsonPropertyName("marketplaces")] + public IList Marketplaces { get => field ??= []; set; } } -/// Wrapper for the singleton's current status. +/// Result of registering a new marketplace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlStatusResult +public sealed class MarketplaceAddResult { - /// State of the runtime-managed remote-control singleton. - [JsonPropertyName("status")] - public RemoteControlStatus Status { get => field ??= new(); set; } + /// Final name of the marketplace as resolved from its manifest. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Reattach to an existing MC session without creating a new one. +/// Marketplace source and optional working directory for relative-path resolution. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlConfigExistingMcSession +internal sealed class PluginsMarketplacesAddRequest { - /// Existing MC session ID to reattach to. - [JsonPropertyName("mcSessionId")] - public string McSessionId { 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; - /// Existing MC task ID for the reattached session. - [JsonPropertyName("mcTaskId")] - public string McTaskId { 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; } } -/// Configuration for the runtime-managed remote-control singleton. +/// Outcome of the remove attempt, including dependent-plugin info when applicable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlConfig +public sealed class MarketplaceRemoveResult { - /// Reattach to an existing MC session without creating a new one. - [JsonPropertyName("existingMcSession")] - public RemoteControlConfigExistingMcSession? ExistingMcSession { 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; } - - /// Whether remote export should be enabled. - [JsonPropertyName("remote")] - public bool Remote { get; set; } - - /// When true, suppresses timeline messages on successful setup. - [JsonPropertyName("silent")] - public bool Silent { get; set; } - - /// Whether the MC session may steer the local session (write mode). - [JsonPropertyName("steerable")] - public bool Steerable { get; set; } + /// Names of installed plugins that prevented removal. Populated only when `removed=false`. + [JsonPropertyName("dependentPlugins")] + public IList? DependentPlugins { get; set; } - /// Existing Mission Control task ID to attach the exported session to. - [JsonPropertyName("taskId")] - public string? TaskId { 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; } } -/// Parameters for attaching the remote-control singleton to a session. +/// Name of the marketplace to remove and an optional force flag. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsStartRemoteControlRequest +internal sealed class PluginsMarketplacesRemoveRequest { - /// Configuration for the runtime-managed remote-control singleton. - [JsonPropertyName("config")] - public RemoteControlConfig Config { get => field ??= new(); 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; } - /// Local session id to attach remote control to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Marketplace name to remove. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Outcome of a transferRemoteControl call. +/// Plugin entry advertised by a marketplace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlTransferResult +public sealed class MarketplacePluginInfo { - /// State of the runtime-managed remote-control singleton. - [JsonPropertyName("status")] - public RemoteControlStatus Status { get => field ??= new(); set; } + /// Short description from the marketplace catalog, when present. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Whether the rebinding actually happened. - [JsonPropertyName("transferred")] - public bool Transferred { get; set; } + /// Plugin name as listed in the marketplace catalog. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Parameters for atomically rebinding the remote-control singleton. +/// Plugins advertised by the marketplace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsTransferRemoteControlRequest +public sealed class MarketplaceBrowseResult { - /// 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; + /// Plugins advertised by the marketplace. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } } -/// Patch for the singleton's steering state. +/// Name of the marketplace whose plugin catalog to fetch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsSetRemoteControlSteeringRequest +internal sealed class PluginsMarketplacesBrowseRequest { - /// 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; } + /// Marketplace name to browse. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Outcome of a stopRemoteControl call. +/// Per-marketplace refresh result, including marketplace name, success flag, and optional failure error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteControlStopResult +public sealed class MarketplaceRefreshEntry { - /// State of the runtime-managed remote-control singleton. - [JsonPropertyName("status")] - public RemoteControlStatus Status { get => field ??= new(); set; } - - /// Whether the singleton was actually torn down by this call. - [JsonPropertyName("stopped")] - public bool Stopped { get; set; } -} + /// Error message (failure only). + [JsonPropertyName("error")] + public string? Error { get; set; } -/// RPC data type for SessionsStopRemoteControl operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionsStopRemoteControlRequest -{ - /// 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; } + /// Marketplace name that was refreshed. + [JsonPropertyName("name")] + public string Name { 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; } + /// Whether the refresh succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Params to attach or detach an in-process ExtensionController delegate. +/// Result of refreshing one or more marketplace catalogs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConfigureSessionExtensionsParams +public sealed class MarketplaceRefreshResult { - /// Session to attach the extension controller delegate to. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Per-marketplace refresh results in deterministic order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// Outcome of an agentRegistry.spawn call. -/// Polymorphic base type discriminated by kind. +/// RPC data type for PluginsMarketplacesRefresh operations. [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 +internal sealed class PluginsMarketplacesRefreshRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. + [JsonPropertyName("name")] + public string? Name { 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). +/// 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 AgentRegistryLiveTargetEntry +public sealed class AgentInfo { - /// 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; } - - /// Copilot CLI version that wrote the entry. - [JsonPropertyName("copilotVersion")] - public string CopilotVersion { get; set; } = string.Empty; + /// Description of the agent's purpose. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Working directory of the session (when known). - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Whether model-driven invocation is disabled for this agent. + [JsonPropertyName("disableModelInvocation")] + public bool? DisableModelInvocation { get; set; } - /// Bind host for the entry's JSON-RPC server. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// Human-readable display name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Process kind tag for the registry entry. - [JsonPropertyName("kind")] - public AgentRegistryLiveTargetEntryKind Kind { get; set; } + /// Declarative hook actions keyed by event name, executed only while this agent is active. + [JsonPropertyName("hooks")] + public IDictionary>? Hooks { get; set; } - /// Wall-clock milliseconds since the watcher last observed this entry (heartbeat freshness). - [JsonPropertyName("lastSeenMs")] - public long LastSeenMs { 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; - /// How the most recent turn ended (clean vs aborted). Lets the renderer distinguish done from done_cancelled. - [JsonPropertyName("lastTerminalEvent")] - public AgentRegistryLiveTargetEntryLastTerminalEvent? LastTerminalEvent { get; set; } + /// 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; } - /// Model identifier currently selected for the session. + /// 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; } - /// Operating-system pid of the process owning this entry. - [JsonPropertyName("pid")] - public long Pid { get; set; } + /// Whether authored models are preferences or required constraints. + [JsonPropertyName("modelPolicy")] + public AgentModelPolicy? ModelPolicy { get; set; } - /// TCP port the entry's JSON-RPC server is listening on. - [JsonPropertyName("port")] - public long Port { 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; } - /// Registry entry schema version (1 = ui-server, 2 = managed-server). - [JsonPropertyName("schemaVersion")] - public long SchemaVersion { get; set; } + /// Name of the agent. Use `id` as the stable selection identifier. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Session ID of the foreground session for this entry. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// 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; } - /// Friendly session name (when set). - [JsonPropertyName("sessionName")] - public string? SessionName { 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; } - /// ISO 8601 timestamp captured at registration. - [JsonPropertyName("startedAt")] - public string StartedAt { 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; } - /// Coarse lifecycle status of the foreground session. - [JsonPropertyName("status")] - public AgentRegistryLiveTargetEntryStatus? Status { get; set; } + /// Skill names preloaded into this agent's context. Omitted means none. + [JsonPropertyName("skills")] + public IList? Skills { get; set; } - /// Monotonic per-publisher revision counter incremented on every status update. Lets watchers detect transient flips. - [JsonPropertyName("statusRevision")] - public long? StatusRevision { get; set; } + /// Where the agent definition was loaded from. + [JsonPropertyName("source")] + public AgentInfoSource? Source { get; set; } - /// Connection token (null when the target is unauthenticated). - [JsonInclude] - [JsonPropertyName("token")] - internal string? Token { 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; } } -/// Canonical process-log discovery outcome; populated from spawnLiveTarget. +/// Agents discovered across user, project, plugin, and remote sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentRegistryLogCapture +public sealed class ServerAgentList { - /// Whether a canonical process log was discovered for this managed spawn. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// All discovered agents across all sources. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; 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; } +/// Optional project paths to include in agent discovery. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AgentsDiscoverRequest +{ + /// 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; } - /// Categorized reason no canonical process log could be opened (set only when enabled is false). - [JsonPropertyName("openErrorReason")] - public AgentRegistryLogCaptureOpenErrorReason? OpenErrorReason { 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; } - /// Absolute path to the managed spawn's process-<timestamp>-<pid>.log file (only set when enabled). - [JsonPropertyName("path")] - public string? Path { 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; } } -/// Managed-server child was spawned and registered successfully. -/// The spawned variant of . +/// Canonical directory where custom agents can be discovered or created, with scope, preference, and optional project path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultSpawned : AgentRegistrySpawnResult +public sealed class AgentDiscoveryPath { - /// - [JsonIgnore] - public override string Kind => "spawned"; + /// Absolute path of the search/create directory (may not exist on disk yet). + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// 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; } + /// 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; } - /// 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; } + /// The input project path this directory was derived from (only for project scope). + [JsonPropertyName("projectPath")] + public string? ProjectPath { 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; } + /// Which tier this directory belongs to. + [JsonPropertyName("scope")] + public AgentDiscoveryPathScope Scope { get; set; } } -/// `child_process.spawn` itself failed before the child entered the registry. -/// The spawn-error variant of . +/// Canonical locations where custom agents can be created so the runtime will recognize them. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultSpawnError : AgentRegistrySpawnResult +public sealed class AgentDiscoveryPathList { - /// - [JsonIgnore] - public override string Kind => "spawn-error"; - - /// Underlying errno code (e.g. ENOENT, EACCES) when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("code")] - public string? Code { get; set; } - - /// Human-readable error message. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Canonical agent create/discovery directories, in priority order. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } } -/// Spawn succeeded but the child did not publish a matching managed-server entry within the timeout. -/// The registry-timeout variant of . +/// Optional project paths to include when enumerating agent discovery directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultRegistryTimeout : AgentRegistrySpawnResult +public sealed class AgentsGetDiscoveryPathsRequest { - /// - [JsonIgnore] - public override string Kind => "registry-timeout"; + /// 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; } - /// Process ID of the orphaned child (so the caller can offer 'kill the pid' guidance). - [JsonPropertyName("childPid")] - public required long ChildPid { get; 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; } - /// Per-spawn log-capture outcome; populated from spawnLiveTarget. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("logCapture")] - public AgentRegistryLogCapture? LogCapture { 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; } } -/// Synchronous pre-validation rejected the spawn request. -/// The validation-error variant of . +/// The agents this runtime ships, named so a consumer can tell them apart from authored ones. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AgentRegistrySpawnResultValidationError : AgentRegistrySpawnResult +internal sealed class AgentsGetBuiltinsResult { - /// - [JsonIgnore] - public override string Kind => "validation-error"; - - /// Which parameter field was invalid. Omitted when the rejection is not field-specific. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("field")] - public AgentRegistrySpawnValidationErrorField? Field { get; 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; } - /// Human-readable explanation; safe to surface in the UI banner. Never logged to unrestricted telemetry. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Every agent name this runtime ships. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; 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; } + /// 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; } } -/// Inputs to spawn a managed-server child via the controller's spawn delegate. +/// A shipped agent, named and described. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentRegistrySpawnRequest +internal sealed class BuiltinAgentSummary { - /// 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; } + /// One-line description of what the agent does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Friendly session name. Must satisfy validateSessionName: non-empty, no leading/trailing whitespace, <=100 chars, no control chars, no double quotes. + /// The agent name, as it appears in `getBuiltins`. [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; } + public string Name { get; set; } = string.Empty; } -/// Feature availability. +/// The shipped agents available under the requested flags. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryCapabilities +internal sealed class AgentsGetAvailableBuiltinsResult { - /// API version. - [JsonPropertyName("apiVersion")] - public long ApiVersion { get; set; } - - /// 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; } + /// Available shipped agents, in the runtime's own order. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// Account metadata. +/// The feature flags to evaluate shipped agents against. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryAuthInfo +internal sealed class AgentsGetAvailableBuiltinsRequest { - /// Host. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// 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; } - /// Login. - [JsonPropertyName("login")] - public string Login { 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; } - /// Authentication type. - [JsonPropertyName("type")] - public AuthInfoType Type { 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; } } -/// Eligible account. +/// One shipped agent's definition. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryAccount +internal sealed class AgentsGetBuiltinDefinitionResult { - /// Opaque account ID. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; - - /// Account metadata. - [JsonPropertyName("authInfo")] - public ConnectorDiscoveryAuthInfo AuthInfo { get => field ??= new(); set; } + /// 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; } -/// Eligible accounts. +/// The shipped agent whose definition to load. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryAccountList +internal sealed class AgentsGetBuiltinDefinitionRequest { - /// Eligible accounts. - [JsonPropertyName("accounts")] - public IList Accounts { get => field ??= []; set; } - - /// Availability. - [JsonPropertyName("availability")] - public ConnectorDiscoveryAvailability Availability { get; set; } + /// 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; } -/// Entry. +/// One shipped agent, projected for a listing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDiscoveryCatalogEntry +internal sealed class AgentsGetBuiltinListingDefinitionResult { - /// Description. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Logo. - [JsonPropertyName("logo")] - public string? Logo { get; set; } + /// 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; +} - /// Name. +/// The shipped agent whose listing entry to load. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AgentsGetBuiltinListingDefinitionRequest +{ + /// 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; - - /// 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; } } -/// Entries for the selected account. +/// 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 ConnectorDiscoveryCatalogResult +internal sealed class AgentsCustomAgentInitialModelDecisionResult { - /// Opaque account ID. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; - - /// Entries. - [JsonPropertyName("connectors")] - public IList Connectors { 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; } - /// Refresh time in Unix epoch milliseconds. - [JsonPropertyName("refreshedAtMs")] - public long RefreshedAtMs { get; 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; } - /// Revision. - [JsonPropertyName("revision")] - public long Revision { 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; } } -/// Selected account. +/// The models a custom agent asks for, and the models actually available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorDiscoveryAccountRequest +internal sealed class AgentsCustomAgentInitialModelDecisionParams { - /// 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; -} + /// 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; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSuspendRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } -/// Result of sending a user message. +/// 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 SendResult +public sealed class InstalledPlugin { - /// Unique identifier assigned to the message. - [JsonPropertyName("messageId")] - public string MessageId { get; set; } = string.Empty; -} + /// Path where the plugin is cached locally. + [JsonPropertyName("cache_path")] + public string? CachePath { get; set; } -/// 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 = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ResponseFormatJsonSchema), "json_schema")] -public partial class ResponseFormat -{ - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Whether the plugin is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + /// Installation timestamp. + [JsonPropertyName("installed_at")] + public string InstalledAt { 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. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class JsonSchemaResponseFormat -{ - /// Optional description passed to OpenAI providers. - [JsonPropertyName("description")] - public string? Description { 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; } - /// Name of the output schema, subject to the provider's naming restrictions. + /// 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; - /// 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; } + /// Source for direct repo installs (when marketplace is empty). + [JsonPropertyName("source")] + public JsonElement? Source { 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; } + /// 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; } + + /// Version installed (if available). + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// The json_schema variant of . +/// 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)] -public partial class ResponseFormatJsonSchema : ResponseFormat +internal sealed class LoggedInUser { - /// - [JsonIgnore] - public override string Type => "json_schema"; + /// Source account this account was derived from, when one was recorded. + [JsonPropertyName("derivedFrom")] + public string? DerivedFrom { get; set; } - /// JSON Schema and provider options for the turn's output. - [JsonPropertyName("jsonSchema")] - public required JsonSchemaResponseFormat JsonSchema { 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; } + + /// Account login on that host. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; } -/// Parameters for sending a user message to the session. +/// 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 SendRequest +internal sealed class GlobalStateLoadResult { - /// 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; } + /// Whether the app tip has been shown. + [JsonPropertyName("appTipShown")] + public bool? AppTipShown { get; set; } - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the message. - [JsonPropertyName("attachments")] - public IList? Attachments { 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; } - /// If false, this message will not trigger a Premium Request Unit charge. User messages default to billable. - [JsonPropertyName("billable")] - public bool? Billable { 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; } - /// If provided, this is shown in the timeline instead of `prompt`. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// When the host first ran on this machine. + [JsonPropertyName("firstLaunchAt")] + public string? FirstLaunchAt { 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; } + /// Plugins installed on this machine. + [JsonPropertyName("installedPlugins")] + public IList? InstalledPlugins { get; set; } - /// If true, adds the message to the front of the queue instead of the end. - [JsonPropertyName("prepend")] - public bool? Prepend { get; set; } + /// Account used for the most recent sign-in. + [JsonPropertyName("lastLoggedInUser")] + public LoggedInUser? LastLoggedInUser { get; set; } - /// The user message text. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Every account the host has signed in to on this machine. + [JsonPropertyName("loggedInUsers")] + public IList? LoggedInUsers { 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; } + /// Whether the one-off cleanup of stored reasoning summaries has run. + [JsonPropertyName("reasoningSummariesCleanupDone")] + public bool? ReasoningSummariesCleanupDone { 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; } + /// Models the user selected recently, most recent first. + [JsonPropertyName("recentModelIds")] + public IList? RecentModelIds { 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; } + /// Whether the user declined to trust the sandbox credential proxy CA. + [JsonPropertyName("sandboxCredentialProxyCaDeclined")] + public bool? SandboxCredentialProxyCaDeclined { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the sandbox onboarding has been shown. + [JsonPropertyName("sandboxOnboardingShown")] + public bool? SandboxOnboardingShown { 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; } + /// Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only behavior. + [JsonPropertyName("staff")] + public bool? Staff { get; set; } - /// W3C Trace Context traceparent header for distributed tracing of this agent turn. - [JsonPropertyName("traceparent")] - public string? Traceparent { get; set; } + /// Whether the user was recognized as GitHub staff. + [JsonPropertyName("staffGithub")] + public bool? StaffGitHub { get; set; } - /// W3C Trace Context tracestate header for distributed tracing. - [JsonPropertyName("tracestate")] - public string? Tracestate { get; set; } + /// When the staff-only log level migration last ran. + [JsonPropertyName("staffLogLevelMigrationAt")] + public string? StaffLogLevelMigrationAt { 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; } + /// 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; } } -/// Result of sending zero or more user messages. +/// Selects the configuration directory whose machine-wide state to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SendMessagesResult +internal sealed class GlobalStateLoadForConfigDirRequest { - /// 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; } + /// 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; } } -/// A single user message to append to the session as part of a `session.sendMessages` turn. +/// 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)] -public sealed class SendMessageItem +internal sealed class GlobalStateWriteKeyRequest { - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with this message. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// 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; } - /// 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; } + /// 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; - /// If provided, this is shown in the timeline instead of `prompt`. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { 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; } +} - /// The user message text. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; +/// A literal choice the command input accepts, with a human-facing description. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SlashCommandInputChoice +{ + /// Human-readable description shown alongside the choice. + [JsonPropertyName("description")] + public string Description { 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; } + /// The literal choice value (e.g. 'on', 'off', 'show'). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// 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. +/// Optional unstructured input hint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendMessagesRequest +public sealed class SlashCommandInput { - /// 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; } + /// 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; } - /// 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; } + /// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). + [JsonPropertyName("completion")] + public SlashCommandInputCompletion? Completion { 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; } + /// Hint to display when command input has not been provided. + [JsonPropertyName("hint")] + public string Hint { get; set; } = string.Empty; - /// If true, adds the messages to the front of the queue instead of the end. - [JsonPropertyName("prepend")] - public bool? Prepend { get; set; } + /// 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; } - /// 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; } + /// When true, the command requires non-empty input; clients should render the input hint as required. + [JsonPropertyName("required")] + public bool? Required { 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; } +/// Slash-command metadata with name, aliases, description, kind, input hint, execution allowance, and schedulability. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SlashCommandInfo +{ + /// Canonical aliases without leading slashes. + [JsonPropertyName("aliases")] + public IList? Aliases { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the command may run while an agent turn is active. + [JsonPropertyName("allowDuringAgentExecution")] + public bool AllowDuringAgentExecution { get; set; } - /// W3C Trace Context traceparent header for distributed tracing of this agent turn. - [JsonPropertyName("traceparent")] - public string? Traceparent { get; set; } + /// Human-readable command description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// W3C Trace Context tracestate header for distributed tracing. - [JsonPropertyName("tracestate")] - public string? Tracestate { get; set; } + /// Whether the command is experimental. + [JsonPropertyName("experimental")] + public bool? Experimental { 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; } -} + /// Optional unstructured input hint. + [JsonPropertyName("input")] + public SlashCommandInput? Input { get; set; } -/// Internal request for sending a system notification. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendSystemNotificationRequest -{ - /// Optional structured notification kind. + /// Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command. [JsonPropertyName("kind")] - public JsonElement? Kind { get; set; } + public SlashCommandKind Kind { get; set; } - /// Notification text to deliver to the model. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Canonical command name without a leading slash. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Internal delivery options, including passive policy. - [JsonPropertyName("options")] - public JsonElement? Options { get; set; } + /// 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; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// Slash commands available in the session, after applying any include/exclude filters. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CommandList +{ + /// Commands available in this session. + [JsonPropertyName("commands")] + public IList Commands { get => field ??= []; set; } } -/// Result of aborting the current turn. +/// Owner, name, and host of a GitHub repository, as resolved from a git remote URL. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AbortResult +public sealed class GitHubRepositoryIdentity { - /// Error message if the abort failed. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Host the remote points at, for example `github.com` or a GitHub Enterprise hostname. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Whether the abort completed successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// 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; } -/// Parameters for aborting the current turn. +/// 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 AbortRequest +internal sealed class GitHubRepositoryAtPathResult { - /// Finite reason code describing why the current turn was aborted. - [JsonPropertyName("reason")] - public AbortReason? Reason { get; set; } + /// Resolved repository identity, or null when the selected remote resolves to no GitHub host. + [JsonPropertyName("repository")] + public GitHubRepositoryIdentity? Repository { get; set; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// Working-tree path whose owning GitHub repository should be resolved. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitHubRepositoryAtPathRequest +{ + /// 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; } -/// Result of interrupting the main agent turn. +/// 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)] -public sealed class InterruptMainTurnResult +internal sealed class GitHubOwnersRequestIdResult { - /// Whether an in-flight main agent turn was interrupted. False when the main loop was not processing. - [JsonPropertyName("interrupted")] - public bool Interrupted { get; set; } + /// Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// Parameters for interrupting the main agent turn. +/// A GitHub login the authenticated user may act as: their own account, or an organization they belong to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class InterruptMainTurnRequest +internal sealed class GitHubOwnerOption { - /// 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; } + /// The owner's GitHub login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } -/// Identifies the target session. +/// 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)] -internal sealed class SessionCancelAllBackgroundAgentsRequest +internal sealed class GitHubOwnersListResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Parameters for shutting down the session. +/// Credential to list owners under, and the request id that makes the listing cancellable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShutdownRequest +internal sealed class GitHubOwnersListRequest { - /// 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; } + /// 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; } - /// Optional human-readable reason. Typically the message of the error that triggered shutdown when type is 'error'. - [JsonPropertyName("reason")] - public string? Reason { 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; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// Whether the id named a running owner listing. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitHubOwnersCancelResult +{ + /// 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; } +} - /// Why the session is being shut down. Defaults to "routine" when omitted. - [JsonPropertyName("type")] - public ShutdownType? Type { get; set; } +/// The owner listing to abandon. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitHubOwnersCancelRequest +{ + /// Request id the listing was started with. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } } -/// Identifier of the session event that was emitted for the log message. +/// The remote the checked-out branch tracks. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LogResult +internal sealed class GitCurrentBranchRemoteResult { - /// The unique identifier of the emitted session event. - [JsonPropertyName("eventId")] - public Guid EventId { get; 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; } -/// Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip. +/// Working-tree path a git query applies to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LogRequest +internal sealed class GitCwdRequest { - /// When true, the message is transient and not persisted to the session event log on disk. - [JsonPropertyName("ephemeral")] - public bool? Ephemeral { get; set; } + /// 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; +} - /// Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". - [JsonPropertyName("level")] - public SessionLogLevel? Level { get; set; } +/// 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; } - /// Human-readable message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Current git branch name. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Current working directory path. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; - /// Optional actionable tip displayed alongside the message. Only honored on `level: "info"`. - [JsonPropertyName("tip")] - public string? Tip { get; set; } + /// Root directory of the git repository, resolved via git rev-parse. + [JsonPropertyName("gitRoot")] + public string? GitRoot { 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; } + /// Head commit of the current git branch. + [JsonPropertyName("headCommit")] + public string? HeadCommit { 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; } + /// 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; } } -/// Adapter-declared policy that tells clients whether discovery may run automatically. +/// A GitHub repository one of a working tree's remotes points at. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAutomaticDiscoveryPolicy +internal sealed class GitRemoteRepository { - /// Whether automatic discovery is allowed, limited to configured providers, or explicit-only. - [JsonPropertyName("mode")] - public ModelProviderAutomaticDiscoveryMode Mode { get; 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; - /// Maximum network scope used by this adapter during discovery. - [JsonPropertyName("networkScope")] - public ModelProviderDiscoveryNetworkScope NetworkScope { get; set; } + /// Repository name, without the owner. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// 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; } + /// Account or organization owning the repository. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; - /// 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; } + /// 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; } -/// An operation supported by a model-provider adapter. +/// The GitHub repositories a working tree's remotes point at. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAdapterOperationDescriptor +internal sealed class GitReposFromRemotesResult { - /// 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; + /// 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; } } -/// Contributor attribution, independent of routing identity and authorization. +/// Git working tree whose GitHub remotes should be listed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAttribution +internal sealed class GitReposFromRemotesRequest { - /// 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; } + /// 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; +} - /// Kind of component that supplied the adapter. Attribution does not confer authority. - [JsonPropertyName("source")] - public ModelProviderProvenanceSource Source { get; set; } +/// Indicates whether the calling client was registered as the session filesystem provider. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionFsSetProviderResult +{ + /// Whether the provider was set successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// A normalized model-provider adapter in the session's effective catalog. +/// Optional capabilities declared by the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAdapterDescriptor +public sealed class SessionFsSetProviderCapabilities { - /// 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; + /// Whether the provider supports binary reads and writes through sessionFs.readFileBytes and sessionFs.writeFileBytes. + [JsonPropertyName("binary")] + public bool? Binary { get; set; } - /// Adapter-declared policy for passive and automatic discovery. - [JsonPropertyName("automaticDiscovery")] - public ModelProviderAutomaticDiscoveryPolicy AutomaticDiscovery { get => field ??= new(); set; } + /// Whether the provider supports SQLite query/exists operations. + [JsonPropertyName("sqlite")] + public bool? Sqlite { get; set; } +} - /// Human-readable provider name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; +/// 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 SessionFsSetProviderRequest +{ + /// Optional capabilities declared by the provider. + [JsonPropertyName("capabilities")] + public SessionFsSetProviderCapabilities? Capabilities { get; set; } - /// Operations supported by this provider adapter. - [JsonPropertyName("operations")] - public IList Operations { get => field ??= []; set; } + /// Path conventions used by this filesystem. + [JsonPropertyName("conventions")] + public SessionFsSetProviderConventions Conventions { get; set; } - /// Attribution for the adapter itself. - [JsonPropertyName("provenance")] - public ModelProviderAttribution Provenance { get => field ??= new(); 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; - /// 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; + /// Path within each session's SessionFs where the runtime stores files for that session. + [JsonPropertyName("sessionStatePath")] + public string SessionStatePath { get; set; } = string.Empty; } -/// Normalized model-provider adapter definitions available to the session, not discovered instances. +/// Indicates whether the calling client was registered as the LLM inference provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderAdapterCatalog +public sealed class LlmInferenceSetProviderResult { - /// Available provider adapters ordered by adapterId. - [JsonPropertyName("providers")] - public IList Providers { get => field ??= []; set; } + /// Whether the provider was set successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Identifies the target session. +/// Whether the start frame was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionProvidersGetCatalogRequest +public sealed class LlmInferenceHttpResponseStartResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } } -/// Attribution for the adapter that produced a provider row. +/// Response head. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderProvenance +internal sealed class LlmInferenceHttpResponseStartRequest { - /// 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; } + /// HTTP response headers, preserving multiple values per name. + [JsonPropertyName("headers")] + public IDictionary> Headers { get => field ??= new Dictionary>(); 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; } + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Descriptive provider family that produced this row; not a routing key. - [JsonPropertyName("providerKind")] - public string ProviderKind { get; set; } = string.Empty; + /// HTTP status code. + [JsonPropertyName("status")] + public long Status { get; set; } - /// Kind of component that supplied the adapter. - [JsonPropertyName("source")] - public ModelProviderProvenanceSource Source { get; set; } + /// Optional HTTP status reason phrase. + [JsonPropertyName("statusText")] + public string? StatusText { get; set; } } -/// Serializable reference to a discovered provider instance. +/// Whether the chunk was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderInstanceReference +public sealed class LlmInferenceHttpResponseChunkResult { - /// 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; + /// True when the chunk was matched to a pending request; false when unknown. + [JsonPropertyName("accepted")] + public bool Accepted { get; set; } } -/// A normalized model-provider instance discovered by the runtime. +/// 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 ModelProviderInstance +public sealed class LlmInferenceHttpResponseChunkError { - /// Human-readable instance name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Optional machine-readable error code. + [JsonPropertyName("code")] + public string? Code { 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; } + /// Human-readable failure description. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; +} - /// Transport to use for inference against this instance. - [JsonPropertyName("inferenceTransport")] - public ProviderEndpointTransport? InferenceTransport { 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; } - /// Provider family to use for inference against this instance. - [JsonPropertyName("inferenceType")] - public ProviderEndpointType? InferenceType { 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; - /// Wire API to use for inference against this instance, when required by the provider family. - [JsonPropertyName("inferenceWireApi")] - public ProviderEndpointWireApi? InferenceWireApi { 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; } - /// Attribution for the adapter that produced this instance. - [JsonPropertyName("provenance")] - public ModelProviderProvenance Provenance { get => field ??= new(); 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; } - /// Self-contained reference for subsequent provider operations. - [JsonPropertyName("reference")] - public ModelProviderInstanceReference Reference { get => field ??= new(); set; } + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; } -/// Typed provider-operation outcome. Use the code for control flow and the optional message for display. +/// Pre-resolved working-directory context for session startup. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderOperationOutcome +public sealed class SessionContext { - /// Machine-readable operation outcome. - [JsonPropertyName("code")] - public ModelProviderOperationOutcomeCode Code { get; set; } + /// Active git branch. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Human-readable detail for non-success outcomes. - [JsonPropertyName("message")] - public string? Message { get; set; } -} + /// Most recent working directory for this session. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; -/// Provider instances found by a discovery operation. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderDiscoverResult -{ - /// Discovered provider instances. Empty when passive default discovery finds no reachable provider. - [JsonPropertyName("instances")] - public IList Instances { get => field ??= []; set; } + /// Git repository root, if the cwd was inside a git repo. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } - /// Typed operation outcome. Passive discovery can return `absent` with an empty instance list. - [JsonPropertyName("outcome")] - public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } + /// Repository host type. + [JsonPropertyName("hostType")] + public SessionContextHostType? HostType { get; set; } + + /// Repository slug in `owner/name` form, when known. + [JsonPropertyName("repository")] + public string? Repository { get; set; } } -/// Provider discovery parameters. +/// GitHub repository the remote session belongs to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderDiscoverRequest +public sealed class RemoteSessionMetadataRepository { - /// Opaque adapter identity returned by `session.providers.getCatalog`. - [JsonPropertyName("adapterId")] - public string AdapterId { get; set; } = string.Empty; + /// Branch associated with the remote session. + [JsonPropertyName("branch")] + public string Branch { 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; } + /// Repository name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Repository owner. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Current health information for a provider instance. +/// Remote session metadata for the session to hand off (typically obtained from `sessions.list` with `source: "remote"`). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderStatus +public sealed class RemoteSessionMetadataValue { - /// Normalized provider instance. - [JsonPropertyName("instance")] - public ModelProviderInstance Instance { get => field ??= new(); set; } + /// Most recent working directory context. + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } - /// Typed operation outcome. - [JsonPropertyName("outcome")] - public ModelProviderOperationOutcome Outcome { get => field ??= new(); 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; } - /// Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; + /// 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; } - /// Provider-reported version. - [JsonPropertyName("version")] - public string? Version { get; set; } -} + /// Always true for remote sessions. + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } -/// Provider status request parameters. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderGetStatusRequest -{ - /// Provider instance reference returned by discovery. - [JsonPropertyName("instance")] - public ModelProviderInstanceReference Instance { get => field ??= new(); set; } + /// Last-modified time as an ISO 8601 timestamp. + [JsonPropertyName("modifiedTime")] + public string ModifiedTime { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Optional human-friendly name set via /rename. + [JsonPropertyName("name")] + public string? Name { get; set; } -/// Provider-reported model artifact metadata. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelArtifactDetails -{ - /// Provider-reported model architecture. - [JsonPropertyName("architecture")] - public string? Architecture { get; set; } + /// Pull request number associated with the session. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Provider-reported model families. - [JsonPropertyName("families")] - public IList? Families { get; set; } + /// Backing remote session IDs (most recent first). + [JsonPropertyName("remoteSessionIds")] + public IList RemoteSessionIds { get => field ??= []; set; } - /// Primary model family. - [JsonPropertyName("family")] - public string? Family { get; set; } + /// GitHub repository the remote session belongs to. + [JsonPropertyName("repository")] + public RemoteSessionMetadataRepository Repository { get => field ??= new(); set; } - /// Artifact format, such as `gguf`. - [JsonPropertyName("format")] - public string? Format { get; set; } + /// Original remote resource identifier (task ID or PR node ID). + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Provider-reported parameter count label. - [JsonPropertyName("parameterSize")] - public string? ParameterSize { get; set; } + /// Stable session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Provider-reported quantization label. - [JsonPropertyName("quantization")] - public string? Quantization { get; set; } + /// Deadline (ISO 8601) at which a CLI remote session becomes stale without further heartbeats. + [JsonPropertyName("staleAt")] + public string? StaleAt { get; set; } - /// Provider-reported tokenizer. - [JsonPropertyName("tokenizer")] - public string? Tokenizer { get; set; } -} + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("startTime")] + public string StartTime { get; set; } = string.Empty; -/// A non-fatal provider observation warning. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderWarning -{ - /// Machine-readable warning code. - [JsonPropertyName("code")] - public string Code { get; set; } = string.Empty; + /// Server-side task state returned by GitHub. + [JsonPropertyName("state")] + public string? State { get; set; } - /// Human-readable warning message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// 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; } } -/// 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. +/// `sessions.open` handoff progress update with step, status, and optional message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredModel +public sealed class SessionsOpenProgress { - /// Provider-reported model capabilities. Omitted capability fields are unknown; explicit false values are preserved. - [JsonPropertyName("capabilities")] - public ModelCapabilities Capabilities { get => field ??= new(); set; } + /// Optional step message. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Provider-reported model artifact details. - [JsonPropertyName("details")] - public ModelArtifactDetails Details { get => field ??= new(); set; } + /// Step status. + [JsonPropertyName("status")] + public SessionsOpenProgressStatus Status { get; set; } - /// Provider-reported artifact digest. - [JsonPropertyName("digest")] - public string? Digest { get; set; } + /// Handoff step. + [JsonPropertyName("step")] + public SessionsOpenProgressStep Step { get; set; } +} - /// Provider-native model identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; +/// Result of opening a session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionOpenResult +{ + /// Remote session metadata, present when status is `connected`. + [JsonPropertyName("metadata")] + public RemoteSessionMetadataValue? Metadata { get; set; } - /// Provider-reported last-modified timestamp. - [JsonPropertyName("modifiedAt")] - public DateTimeOffset? ModifiedAt { get; set; } + /// Handoff progress steps, present when status is `handed_off`. + [JsonPropertyName("progress")] + public IList? Progress { get; set; } - /// Provider-reported display name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Remote session ID, present when status is `connected`. + [JsonPropertyName("remoteSessionId")] + public string? RemoteSessionId { get; set; } - /// Attribution for the adapter that produced this model row. - [JsonPropertyName("provenance")] - public ModelProviderProvenance Provenance { get => field ??= new(); set; } + /// Opened session ID. Omitted when status is `not_found`. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } - /// Provider-reported artifact size in bytes. - [JsonPropertyName("sizeBytes")] - public long? SizeBytes { 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; } - /// Non-fatal warnings encountered while enriching this model. - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; set; } + /// Outcome of the open request. + [JsonPropertyName("status")] + public SessionsOpenStatus Status { 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. +/// Identifier and optional friendly name assigned to the newly forked session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredModelList +public sealed class SessionsForkResult { - /// Provider-native models in provider order. - [JsonPropertyName("models")] - public IList Models { get => field ??= []; set; } + /// Friendly name assigned to the forked session, if any. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Typed operation outcome. - [JsonPropertyName("outcome")] - public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } + /// The new forked session's ID. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Provider model inventory request parameters. +/// Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderModelsListRequest +internal sealed class SessionsForkRequest { - /// Provider instance reference returned by discovery. - [JsonPropertyName("instance")] - public ModelProviderInstanceReference Instance { get => field ??= new(); set; } + /// Optional friendly name to assign to the forked session. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Target session identifier. + /// 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; } } -/// Vision-specific limits. +/// Repository associated with the connected remote session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideLimitsVision +public sealed class ConnectedRemoteSessionMetadataRepository { - /// Maximum image size in bytes. - [JsonPropertyName("max_prompt_image_size")] - public long? MaxPromptImageSize { get; set; } + /// Branch associated with the remote session. + [JsonPropertyName("branch")] + public string Branch { get; set; } = string.Empty; - /// Maximum number of images per prompt. - [JsonPropertyName("max_prompt_images")] - public long? MaxPromptImages { get; set; } + /// Repository name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// MIME types the model accepts. - [JsonPropertyName("supported_media_types")] - public IList? SupportedMediaTypes { get; set; } + /// Repository owner or organization login. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Token limits for prompts, outputs, and context window. +/// Metadata for a connected remote session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideLimits +public sealed class ConnectedRemoteSessionMetadata { - /// Maximum total context window size in tokens. - [JsonPropertyName("max_context_window_tokens")] - public long? MaxContextWindowTokens { get; set; } + /// Neutral SDK discriminator for the connected remote session kind. + [JsonPropertyName("kind")] + public ConnectedRemoteSessionMetadataKind Kind { get; set; } - /// Maximum number of output/completion tokens. - [JsonPropertyName("max_output_tokens")] - public long? MaxOutputTokens { get; set; } + /// Last session update time as an ISO 8601 string. + [JsonPropertyName("modifiedTime")] + public DateTimeOffset ModifiedTime { get; set; } - /// Maximum number of prompt/input tokens. - [JsonPropertyName("max_prompt_tokens")] - public long? MaxPromptTokens { get; set; } + /// Optional friendly session name. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Vision-specific limits. - [JsonPropertyName("vision")] - public ModelCapabilitiesOverrideLimitsVision? Vision { get; set; } -} + /// Pull request number associated with the session. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } -/// Feature flags indicating what the model supports. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideSupports -{ - /// 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; } + /// Repository associated with the connected remote session. + [JsonPropertyName("repository")] + public ConnectedRemoteSessionMetadataRepository Repository { get => field ??= new(); set; } - /// Whether this model supports reasoning effort configuration. - [JsonPropertyName("reasoningEffort")] - public bool? ReasoningEffort { get; set; } + /// Original remote resource identifier. + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Whether this model supports canonical tool calling. - [JsonPropertyName("toolCalls")] - public bool? ToolCalls { get; set; } + /// SDK session ID for the connected remote session. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Whether this model supports vision/image input. - [JsonPropertyName("vision")] - public bool? Vision { get; set; } -} + /// Remote session staleness deadline as an ISO 8601 string. + [JsonPropertyName("staleAt")] + public DateTimeOffset? StaleAt { get; set; } -/// Optional capability overrides (vision, tool_calls, reasoning, etc.). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverride -{ - /// Token limits for prompts, outputs, and context window. - [JsonPropertyName("limits")] - public ModelCapabilitiesOverrideLimits? Limits { get; set; } + /// Session start time as an ISO 8601 string. + [JsonPropertyName("startTime")] + public DateTimeOffset StartTime { get; set; } - /// Feature flags indicating what the model supports. - [JsonPropertyName("supports")] - public ModelCapabilitiesOverrideSupports? Supports { get; set; } + /// Remote session state returned by the backing service. + [JsonPropertyName("state")] + public string? State { get; set; } + + /// Optional session summary. + [JsonPropertyName("summary")] + public string? Summary { get; set; } } -/// RPC data type for ProtocolSystemMessageAppendConfig operations. +/// Remote session connection result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageAppendConfig +public sealed class RemoteSessionConnectionResult { - /// Text appended to the standard system prompt. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// Metadata for a connected remote session. + [JsonPropertyName("metadata")] + public ConnectedRemoteSessionMetadata Metadata { get => field ??= new(); set; } - /// Append-mode discriminator. Omission also selects append mode. - [JsonPropertyName("mode")] - public ProtocolAppendMode? Mode { get; set; } + /// SDK session ID for the connected remote session. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for SystemMessageBlock operations. +/// Remote session connection parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SystemMessageBlock +internal sealed class ConnectRemoteSessionParams { - /// 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; - - /// Whether the block is static and may be cached independently of dynamic prompt content. - [JsonPropertyName("isStatic")] - public bool? IsStatic { get; set; } + /// Session ID to connect to. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for ProtocolSystemMessageReplaceConfig operations. +/// 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 ProtocolSystemMessageReplaceConfig +public partial class SessionListEntry { - /// Complete replacement system-message text. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; - - /// Optional structured blocks corresponding to the replacement content. - [JsonPropertyName("contentBlocks")] - public IList? ContentBlocks { get; set; } + /// The boolean discriminator. + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } - /// Replace-mode discriminator. - [JsonPropertyName("mode")] - public ProtocolReplaceMode Mode { get; set; } -} + /// Runtime client name that created/last resumed this session. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } -/// RPC data type for ProtocolStaticSectionOverride operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolStaticSectionOverride -{ - /// Declarative operation applied to the section. - [JsonPropertyName("action")] - public ProtocolStaticSectionAction Action { get; set; } + /// Pre-resolved working-directory context for session startup. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } - /// Optional content used by replace, append, and prepend operations. - [JsonPropertyName("content")] - public string? Content { 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; } -/// Polymorphic base type discriminated by action. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ProtocolMarkerSectionOverrideTransform), "transform")] -[JsonDerivedType(typeof(ProtocolMarkerSectionOverridePreserve), "preserve")] -public partial class ProtocolMarkerSectionOverride -{ - /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; -} + /// 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; } + /// True for detached maintenance sessions that should be hidden from normal resume lists. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("isDetached")] + public bool? IsDetached { get; set; } -/// The transform variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ProtocolMarkerSectionOverrideTransform : ProtocolMarkerSectionOverride -{ - /// - [JsonIgnore] - public override string Action => "transform"; -} + /// 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 preserve variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ProtocolMarkerSectionOverridePreserve : ProtocolMarkerSectionOverride -{ - /// - [JsonIgnore] - public override string Action => "preserve"; -} + /// Last-modified time of the session's persisted state, as ISO 8601. + [JsonPropertyName("modifiedTime")] + public required string ModifiedTime { get; set; } -/// 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; } + /// Optional human-friendly name set via /rename. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Gets the value when this instance contains . - public ProtocolMarkerSectionOverride? ProtocolMarkerSectionOverride { get; } + /// Pull request number associated with the session. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Initializes a new instance of the class from . - public ProtocolSectionOverride(ProtocolStaticSectionOverride value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolStaticSectionOverride = value; - } + /// Backing remote session IDs (most recent first). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("remoteSessionIds")] + public IList? RemoteSessionIds { get; set; } - /// Converts to . - public static implicit operator ProtocolSectionOverride(ProtocolStaticSectionOverride value) => new(value); + /// GitHub repository the remote session belongs to. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("repository")] + public RemoteSessionMetadataRepository? Repository { get; set; } - /// Initializes a new instance of the class from . - public ProtocolSectionOverride(ProtocolMarkerSectionOverride value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolMarkerSectionOverride = value; - } + /// Original remote resource identifier (task ID or PR node ID). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } - /// Converts to . - public static implicit operator ProtocolSectionOverride(ProtocolMarkerSectionOverride value) => new(value); + /// Stable session identifier. + [JsonPropertyName("sessionId")] + public required string SessionId { get; set; } - /// 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."); - } + /// 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; } - 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); - } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("startTime")] + public required string StartTime { get; set; } - throw new JsonException("JSON value did not match any ProtocolSectionOverride variant."); - } + /// Server-side task state returned by GitHub. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("state")] + public string? State { 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; - } + /// Short summary of the session, when one has been derived. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("summary")] + public string? Summary { get; set; } - throw new JsonException("No ProtocolSectionOverride variant value is set."); - } - } + /// Whether the remote task originated from CCA or CLI `--remote`. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("taskType")] + public RemoteSessionMetadataTaskType? TaskType { get; set; } } -/// RPC data type for ProtocolSystemMessageCustomizeConfig operations. +/// Sessions matching the filter, ordered most-recently-modified first. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageCustomizeConfig +public sealed class SessionList { - /// Text appended after the customized sections. - [JsonPropertyName("content")] - public string? Content { get; set; } - - /// Customize-mode discriminator. - [JsonPropertyName("mode")] - public ProtocolCustomizeMode Mode { get; set; } - - /// Named standard-prompt section overrides. - [JsonPropertyName("sections")] - public IDictionary? Sections { get; set; } + /// Sessions ordered most-recently-modified first. Discriminated by `isRemote`. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } } -/// JSON union data type for ProtocolSystemMessageConfig. +/// Optional filter applied to the returned sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -public sealed partial class ProtocolSystemMessageConfig +public sealed class SessionListFilter { - /// 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; - } + /// Match sessions whose context.branch equals this value. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) => new(value); + /// Match sessions whose context.cwd equals this value. + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageCustomizeConfig = value; - } + /// Match sessions whose context.gitRoot equals this value. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) => new(value); + /// Match sessions whose context.repository equals this value. + [JsonPropertyName("repository")] + public string? Repository { get; set; } +} - /// 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."); - } +/// 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; } - 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); - } + /// When true, include detached maintenance sessions. Defaults to false for user-facing session lists. + [JsonPropertyName("includeDetached")] + public bool? IncludeDetached { get; set; } - throw new JsonException("JSON value did not match any ProtocolSystemMessageConfig variant."); - } + /// 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; } - /// - 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; - } + /// Which session sources to include. Defaults to `local` for backward compatibility. + [JsonPropertyName("source")] + public SessionSource? Source { get; set; } - throw new JsonException("No ProtocolSystemMessageConfig variant value is 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; } } -/// A BYOK model definition referencing a named provider. +/// Persisted local session metadata, including identifiers, timestamps, summary/name, client, context, detached state, and task ID. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderModelConfig +public sealed class LocalSessionMetadataValue { - /// 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; + /// Runtime client name that created/last resumed this session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// Maximum context window tokens for the model. - [JsonPropertyName("maxContextWindowTokens")] - public double? MaxContextWindowTokens { get; set; } + /// Pre-resolved working-directory context for session startup. + [JsonPropertyName("context")] + public SessionContext? Context { get; set; } - /// Maximum output tokens for the model. - [JsonPropertyName("maxOutputTokens")] - public double? MaxOutputTokens { get; set; } + /// True for detached maintenance sessions that should be hidden from normal resume lists. + [JsonPropertyName("isDetached")] + public bool? IsDetached { get; set; } - /// Maximum prompt/input tokens for the model. - [JsonPropertyName("maxPromptTokens")] - public double? MaxPromptTokens { get; set; } + /// Always false for local sessions. + [JsonPropertyName("isRemote")] + public bool IsRemote { get; set; } - /// Provider-published model metadata, preserved verbatim as the public Model.metadata object. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { 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; } - /// Well-known base model id used for behavior/capability/config lookup. Defaults to `id`. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Last-modified time of the session's persisted state, as ISO 8601. + [JsonPropertyName("modifiedTime")] + public string ModifiedTime { get; set; } = string.Empty; - /// Display name for model pickers. Defaults to the provider-qualified selection id (`provider/id`). + /// Optional human-friendly name set via /rename. [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; + /// Stable session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { 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; } + /// Session creation time as an ISO 8601 timestamp. + [JsonPropertyName("startTime")] + public string StartTime { get; set; } = string.Empty; - /// The model name sent to the provider API for inference. Defaults to `id`. - [JsonPropertyName("wireModel")] - public string? WireModel { get; set; } + /// Short summary of the session, when one has been derived. + [JsonPropertyName("summary")] + public string? Summary { get; set; } } -/// Azure-specific provider options. +/// Persisted local session metadata when the session exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderConfigAzure +internal sealed class SessionsGetMetadataResult { - /// API version. When set, uses the versioned deployment route. When omitted, uses the GA versionless v1 route. - [JsonPropertyName("apiVersion")] - public string? ApiVersion { get; set; } + /// Local session metadata, omitted when the session does not exist. + [JsonPropertyName("session")] + public LocalSessionMetadataValue? Session { get; set; } } -/// External SDK input for a named custom model provider. Ingested by the native protocol boundary before host dispatch. +/// Session ID whose persisted metadata should be read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NamedProviderConfig +internal sealed class SessionsGetMetadataRequest { - /// 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; } - - /// Wire API used to communicate with the provider. - [JsonPropertyName("wireApi")] - public ProviderConfigWireApi? WireApi { get; set; } + /// Session ID to inspect. + [JsonPropertyName("sessionId")] + public string SessionId { 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. +/// Client metadata outcome for one requested local session. +/// Polymorphic base type discriminated by status. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderConfigurationPlan +[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 { - /// 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; } - - /// 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; } - - /// 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 `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; } + /// The type discriminator. + [JsonPropertyName("status")] + public virtual string Status { get; set; } = string.Empty; } -/// 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`. + +/// The ok variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelProviderPrepareConfigurationRequest +public partial class SessionsClientMetadataEntryOk : SessionsClientMetadataEntry { - /// The discovered instance that serves the model. - [JsonPropertyName("instance")] - public ModelProviderInstance Instance { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Status => "ok"; - /// The discovered model to configure. - [JsonPropertyName("model")] - public DiscoveredModel Model { get => field ??= new(); set; } + /// Validated client metadata, possibly empty or projected to requested keys. + [JsonPropertyName("metadata")] + public required IDictionary Metadata { get; set; } - /// Target session identifier. + /// Requested session ID. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } } -/// Managed sandbox enforcement state for a session. +/// The notFound variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxEnforcementStatus +public partial class SessionsClientMetadataEntryNotFound : SessionsClientMetadataEntry { - /// 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; } + /// + [JsonIgnore] + public override string Status => "notFound"; - /// Whether the effective managed policy requires an available sandbox backend. - [JsonPropertyName("required")] - public bool Required { get; set; } + /// Requested session ID. + [JsonPropertyName("sessionId")] + public required string SessionId { get; set; } } -/// Identifies the target session. +/// The corrupt variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSandboxGetEnforcementStatusRequest +public partial class SessionsClientMetadataEntryCorrupt : SessionsClientMetadataEntry { - /// Target session identifier. + /// + [JsonIgnore] + public override string Status => "corrupt"; + + /// Requested session ID. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } } -/// A possible secret-bearing environment variable and optional locally suggested HTTPS injection hosts. This is a draft for user review, not an active grant. +/// The unsupportedVersion variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxCredentialSuggestion +public partial class SessionsClientMetadataEntryUnsupportedVersion : SessionsClientMetadataEntry { - /// Environment variable name. The secret value is never returned. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Status => "unsupportedVersion"; - /// 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; } + /// Requested session ID. + [JsonPropertyName("sessionId")] + public required string SessionId { get; set; } } -/// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxCredentialSuggestionsResult +public partial class SessionsClientMetadataEntryUnavailable : SessionsClientMetadataEntry { - /// Candidates for user review. Empty when sandboxing is disabled or no unconfigured candidates are present. - [JsonPropertyName("suggestions")] - public IList Suggestions { get => field ??= []; set; } -} + /// + [JsonIgnore] + public override string Status => "unavailable"; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSandboxGetCredentialSuggestionsRequest -{ - /// Target session identifier. + /// 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; } + + /// Requested session ID. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string SessionId { get; set; } } -/// Result of attempting to disable sandboxing for the current session. +/// Bounded batch request for client-owned metadata from persisted local sessions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxDisableForSessionResult +internal sealed class SessionsGetClientMetadataRequest { - /// The authoritative sandbox enabled state after the operation. - [JsonPropertyName("enabled")] - public bool Enabled { 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; } - /// Whether this call resolved the pending request and applied the session opt-out. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Session IDs to inspect. Results preserve this order. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } } -/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +/// Batch of session events returned by a read, with cursor and continuation metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionDecisionContext +public sealed class EventsReadResult { - /// Disposition of the permission request as observed by the responding client. - [JsonPropertyName("outcome")] - public PermissionDecisionOutcome Outcome { get; set; } + /// 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; - /// 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; } + /// 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; } - /// Controlled reason or actor responsible for the response. - [JsonPropertyName("source")] - public PermissionDecisionSource Source { 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; } - /// Client surface that submitted the response. - [JsonPropertyName("surface")] - public PermissionDecisionSurface Surface { 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; } } -/// Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. +/// Pagination options for reading an inactive or active local session's persisted event journal. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxDisableForSessionRequest +internal sealed class SessionsReadPersistedEventsRequest { - /// Optional attribution for the permission decision. - [JsonPropertyName("decisionContext")] - public PermissionDecisionContext? DecisionContext { 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; } - /// Identifier of the exact pending sandbox-bypass permission request that authorized the session opt-out. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// 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; } - /// Target session identifier. + /// 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; } + + /// Session ID whose persisted event journal should be read. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of accepting a sandbox path grant. +/// Recent local session IDs that contain user-visible history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxGrantPathForRequestResult +internal sealed class SessionsListNonEmptySessionIdsResult { - /// Whether this call resolved the pending request and added the path to the session's sandbox policy. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Session IDs ordered newest-first. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } } -/// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. +/// Limit for non-empty local session IDs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxGrantPathForRequestRequest +internal sealed class SessionsListNonEmptySessionIdsRequest { - /// 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; + /// Maximum number of session IDs to return. + [JsonPropertyName("limit")] + public long? Limit { get; set; } +} - /// Target session identifier. +/// ID of the local session bound to the given GitHub task, or omitted when none. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionsFindByTaskIDResult +{ + /// Omitted when no local session is bound to that GitHub task. [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public string? SessionId { get; set; } } -/// Authentication status and account metadata for the session. +/// GitHub task ID to look up. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionAuthStatus +internal sealed class SessionsFindByTaskIDRequest { - /// Authentication type. - [JsonPropertyName("authType")] - public AuthInfoType? AuthType { get; set; } + /// GitHub task ID to look up. + [JsonPropertyName("taskId")] + public string TaskId { get; set; } = string.Empty; +} - /// Copilot plan tier (e.g., individual_pro, business). - [JsonPropertyName("copilotPlan")] - public string? CopilotPlan { get; set; } +/// 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; } +} - /// Authentication host URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public string? Host { get; 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; +} - /// Whether the session has resolved authentication. - [JsonPropertyName("isAuthenticated")] - public bool IsAuthenticated { get; 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; } +} - /// Authenticated login/username, if available. - [JsonPropertyName("login")] - public string? Login { get; set; } +/// 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; } +} - /// Human-readable authentication status description. - [JsonPropertyName("statusMessage")] - public string? StatusMessage { get; set; } +/// Absolute path to the session's events.jsonl file on disk. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsGetEventFilePathResult +{ + /// 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 SessionGitHubAuthGetStatusRequest +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; } -/// Indicates whether the credential update succeeded. +/// Map of sessionId -> on-disk size in bytes for each session's workspace directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSetCredentialsResult +public sealed class SessionSizes { - /// 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; } - - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { 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; } } -/// Authentication credentials accepted by session.gitHubAuth.setCredentials. Session-owned token-provider identities cannot be installed through this method. -/// Polymorphic base type discriminated by type. +/// Session IDs from the input set that are currently in use by another process. [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 +public sealed class SessionsCheckInUseResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { 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; } } - -/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. -/// The hmac variant of . +/// Session IDs to test for live in-use locks. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoHmac : SettableAuthInfo +internal sealed class SessionsCheckInUseRequest { - /// - [JsonIgnore] - public override string Type => "hmac"; - - /// 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; } + /// Session IDs to test for live in-use locks. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; 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 session's persisted remote-steerable flag, or omitted when no value has been persisted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoEnv : SettableAuthInfo +internal sealed class SessionsGetPersistedRemoteSteerableResult { - /// - [JsonIgnore] - public override string Type => "env"; - - /// 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; } - - /// 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 session's persisted remote-steerable flag if recorded; omitted when no value has been persisted. + [JsonPropertyName("remoteSteerable")] + public bool? RemoteSteerable { get; set; } } -/// Token authentication accepted by session.gitHubAuth.setCredentials. -/// The token variant of . +/// Session ID to look up the persisted remote-steerable flag for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoToken : SettableAuthInfo +internal sealed class SessionsGetPersistedRemoteSteerableRequest { - /// - [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; } - - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } - - /// The token value itself. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Session ID to look up the persisted remote-steerable flag for. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. -/// The copilot-api-token variant of . +/// 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 partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo +public sealed class SessionsCloseResult { - /// - [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; } - - /// Authentication host (always the public GitHub host). - [JsonPropertyName("host")] - public required string Host { 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 . +/// Session ID to close. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoUser : SettableAuthInfo +internal sealed class SessionsCloseRequest { - /// - [JsonIgnore] - public override string Type => "user"; - - /// 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; } + /// Session ID to close. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// OAuth user login. - [JsonPropertyName("login")] - public required string Login { 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; } } -/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. -/// The account variant of . +/// Session IDs to close, deactivate, and delete from disk. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoAccount : SettableAuthInfo +internal sealed class SessionsBulkDeleteRequest { - /// - [JsonIgnore] - public override string Type => "account"; + /// Session IDs to close, deactivate, and delete from disk. + [JsonPropertyName("sessionIds")] + public IList SessionIds { get => field ??= []; set; } +} - /// Host coordinate owned by the account's model provider. - [JsonPropertyName("host")] - public required string Host { get; set; } +/// Session ID to delete from disk. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsDeleteRequest +{ + /// Session ID to delete. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Login identifying the provider-owned account. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// Internal resolved session directory path to delete. + [JsonPropertyName("sessionPath")] + public string? SessionPath { get; set; } } -/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. -/// The gh-cli variant of . +/// 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)] -public partial class SettableAuthInfoGhCli : SettableAuthInfo +public sealed class SessionPruneResult { - /// - [JsonIgnore] - public override string Type => "gh-cli"; + /// Session IDs that would be deleted in dry-run mode (always empty otherwise). + [JsonPropertyName("candidates")] + public IList Candidates { get => field ??= []; 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; } + /// Session IDs that were deleted (always empty in dry-run mode). + [JsonPropertyName("deleted")] + public IList Deleted { get => field ??= []; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// True when no deletions were actually performed. + [JsonPropertyName("dryRun")] + public bool DryRun { get; set; } - /// User login as reported by `gh auth status`. - [JsonPropertyName("login")] - public required string Login { get; set; } + /// Total bytes freed (actual when not dry-run, projected when dry-run). + [JsonPropertyName("freedBytes")] + public long FreedBytes { get; set; } - /// The token returned by `gh auth token`. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Session IDs that were skipped (e.g., named sessions). + [JsonPropertyName("skipped")] + public IList Skipped { get => field ??= []; 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 . +/// Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoApiKey : SettableAuthInfo +internal sealed class SessionsPruneOldRequest { - /// - [JsonIgnore] - public override string Type => "api-key"; + /// When true, only report what would be deleted without performing any deletion. + [JsonPropertyName("dryRun")] + public bool? DryRun { get; set; } - /// The API key. Treat as a secret. - [JsonPropertyName("apiKey")] - public required string ApiKey { get; set; } + /// Session IDs that should never be considered for pruning. + [JsonPropertyName("excludeSessionIds")] + public IList? ExcludeSessionIds { 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; } + /// When true, named sessions (set via /rename) are also eligible for pruning. + [JsonPropertyName("includeNamed")] + public bool? IncludeNamed { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Delete sessions whose modifiedTime is at least this many days old. + [JsonPropertyName("olderThanDays")] + public long OlderThanDays { get; set; } } -/// New auth credentials to install on the session. Omit to leave credentials unchanged. +/// 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 SessionSetCredentialsParams +public sealed class SessionsSaveResult { - /// 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. +/// 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; } -/// Credential-free authentication identity safe to expose to hosts and user interfaces. +/// 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 AuthIdentity +public sealed class SessionsReleaseLockResult { - /// 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. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// Authenticated login, when available. - [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; } - - /// Authentication type. - [JsonPropertyName("type")] - public AuthInfoType Type { get; set; } } -/// Identifies the target session. +/// Session ID whose in-use lock should be released. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthGetCurrentAuthInfoRequest +internal sealed class SessionsReleaseLockRequest { - /// Target session identifier. + /// Session ID whose in-use lock should be released. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// 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)] -internal sealed class SessionGitHubAuthGetAllAuthAvailableRequest +public sealed class SessionEnrichMetadataResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Enriched records, with summary and context backfilled. Sessions confirmed empty and unnamed may be omitted. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } } -/// Identifies the target session. +/// Session metadata records to enrich with summary and context information. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthRefreshCopilotUserRequest +internal sealed class SessionsEnrichMetadataRequest { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } } -/// Internal GitHub login parameters. +/// The workspace record that was written. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthLoginRequest +internal sealed class SessionsCreateWorkspaceResult { - /// GitHub host URL. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// The created workspace record, as JSON. + [JsonPropertyName("workspaceJson")] + public string WorkspaceJson { get; set; } = string.Empty; +} - /// GitHub login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; +/// A working-directory context together with the client that produced it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionWorkingDirectoryContextWithClient +{ + /// Merge-base commit SHA. + [JsonPropertyName("baseCommit")] + public string? BaseCommit { get; set; } - /// Whether to persist the token after login. - [JsonPropertyName("persist")] - public bool? Persist { get; set; } + /// Current git branch name. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Name of the client that created the session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// GitHub authentication token. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// Current working directory path. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; + + /// Root directory of the git repository. + [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 string? HostType { get; set; } + + /// Repository identifier derived from the git remote URL. + [JsonPropertyName("repository")] + public string? Repository { get; set; } + + /// Raw host string from the git remote URL. + [JsonPropertyName("repositoryHost")] + public string? RepositoryHost { get; set; } } -/// Parameters for switching the session's active authentication. +/// Identity, state location and starting context for a workspace record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthSwitchRequest +internal sealed class SessionsCreateWorkspaceRequest { - /// Authentication information to activate. - [JsonPropertyName("authInfo")] - public AuthInfo AuthInfo { get => field ??= new(); 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; } - /// Target session identifier. + /// `windows` (any letter case) selects Windows path rules. Any other value selects POSIX path rules. + [JsonPropertyName("convention")] + public string Convention { get; set; } = string.Empty; + + /// User-supplied display name for the workspace. + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Session ID the workspace record belongs to. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Optional token paired with the authentication information. - [JsonPropertyName("token")] - public string? Token { 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; } -/// Identifies the target session. +/// The workspace record on disk, omitted when the session has none. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthLogoutRequest +internal sealed class SessionsLoadWorkspaceResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The workspace record, as JSON. Omitted when the record does not exist. + [JsonPropertyName("workspaceJson")] + public string? WorkspaceJson { get; set; } } -/// Parameters identifying a GitHub authentication to log out. +/// 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 SessionAuthLogoutUserRequest +internal sealed class SessionsLoadWorkspaceRequest { - /// Authentication information to log out. - [JsonPropertyName("authInfo")] - public AuthInfo AuthInfo { get => field ??= new(); set; } - - /// Target session identifier. + /// 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; + + /// Root directory every session's state directory sits under. + [JsonPropertyName("sessionsHome")] + public string SessionsHome { get; set; } = string.Empty; } -/// Validation error from an authentication attempt. +/// 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 AuthValidationError +internal sealed class SessionsUpdateWorkspaceFieldsResult { - /// 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. +/// 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 SessionGitHubAuthLastAuthErrorsRequest +internal sealed class SessionsUpdateWorkspaceFieldsRequest { - /// Target session identifier. + /// 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; + + /// 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; -} -/// 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; + /// Root directory every session's state directory sits under. + [JsonPropertyName("sessionsHome")] + public string SessionsHome { get; set; } = string.Empty; } - -/// One signed-in account in the roster forest. +/// 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 AccountStatus +public sealed class SessionsReloadPluginHooksResult { - /// Whether this is the active account. - [JsonPropertyName("active")] - public bool Active { get; set; } - - /// Human-readable credential source used to authenticate this account, such as `gh` or `GITHUB_TOKEN`. - [JsonPropertyName("authSource")] - public string? AuthSource { 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; - - /// The provider kind of this account. - [JsonPropertyName("kind")] - public AccountKind Kind { get; set; } - - /// Authenticated login/username. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; - - /// Opaque selection id used to switch to, or log out, this account. - [JsonPropertyName("selectionId")] - public string SelectionId { get; set; } = string.Empty; } -/// The accounts variant of . +/// Active session ID and an optional flag for deferring repo-level hooks until folder trust. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateValueAccounts : AuthEnumerateValue +internal sealed class SessionsReloadPluginHooksRequest { - /// - [JsonIgnore] - public override string Kind => "accounts"; + /// When true, skip repo-level hooks. Use before folder trust is confirmed; loadDeferredRepoHooks loads them post-trust. + [JsonPropertyName("deferRepoHooks")] + public bool? DeferRepoHooks { get; set; } - /// The signed-in account forest; empty when not logged in. - [JsonPropertyName("items")] - public required IList Items { get; set; } + /// Active session ID to reload hooks for. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// A provider offered for interactive login. +/// Queued repo-level startup prompts and the total hook command count after loading. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderDescriptor +public sealed class SessionLoadDeferredRepoHooksResult { - /// 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; } + /// 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; } - /// Human-readable menu label, owned by the runtime so every consumer renders identical text. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; + /// 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; } } -/// The providers variant of . +/// Active session ID whose deferred repo-level hooks should be loaded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateValueProviders : AuthEnumerateValue +internal sealed class SessionsLoadDeferredRepoHooksRequest { - /// - [JsonIgnore] - public override string Kind => "providers"; - - /// The providers offered for interactive login. - [JsonPropertyName("items")] - public required IList Items { get; set; } + /// Active session ID whose deferred repo-level hooks should be loaded. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Selects which accounts collection to enumerate. A no-arg selector is the empty-payload variant. -/// Polymorphic base type discriminated by kind. +/// 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)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthEnumerateQueryAccounts), "accounts")] -[JsonDerivedType(typeof(AuthEnumerateQueryProviders), "providers")] -public partial class AuthEnumerateQuery +public sealed class SessionsSetAdditionalPluginsResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; } - -/// The accounts variant of . +/// Manager-wide additional plugins to register; replaces any previously-configured set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateQueryAccounts : AuthEnumerateQuery +internal sealed class SessionsSetAdditionalPluginsRequest { - /// - [JsonIgnore] - public override string Kind => "accounts"; + /// 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; } } -/// The providers variant of . +/// Dynamic-context board entry count, when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateQueryProviders : AuthEnumerateQuery +internal sealed class SessionsGetBoardEntryCountResult { - /// - [JsonIgnore] - public override string Kind => "providers"; - - /// 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; } + /// Board entry count, when available. + [JsonPropertyName("count")] + public long? Count { get; set; } } -/// Enumerate request carrying the typed collection query. +/// Session ID whose board entry count should be returned. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsEnumerateRequest +internal sealed class SessionsGetBoardEntryCountRequest { - /// Which typed accounts collection to enumerate. - [JsonPropertyName("query")] - public AuthEnumerateQuery Query { get => field ??= new(); set; } - - /// Target session identifier. + /// Session ID whose board entry count should be returned. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The read result, keyed by the same selector as the query. -/// Polymorphic base type discriminated by kind. +/// State of the runtime-managed remote-control singleton. +/// Polymorphic base type discriminated by state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", + TypeDiscriminatorPropertyName = "state", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthReadValueActiveAccount), "activeAccount")] -[JsonDerivedType(typeof(AuthReadValueStatus), "status")] -[JsonDerivedType(typeof(AuthReadValueLastErrors), "lastErrors")] -public partial class AuthReadValue +[JsonDerivedType(typeof(RemoteControlStatusOff), "off")] +[JsonDerivedType(typeof(RemoteControlStatusConnecting), "connecting")] +[JsonDerivedType(typeof(RemoteControlStatusActive), "active")] +[JsonDerivedType(typeof(RemoteControlStatusError), "error")] +public partial class RemoteControlStatus { /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + [JsonPropertyName("state")] + public virtual string State { get; set; } = string.Empty; } -/// The activeAccount variant of . +/// Remote control is not connected. +/// The off variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueActiveAccount : AuthReadValue +public partial class RemoteControlStatusOff : RemoteControlStatus { /// [JsonIgnore] - public override string Kind => "activeAccount"; - - /// The active account, or absent when not logged in. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("account")] - public AccountStatus? Account { 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; } + public override string State => "off"; } -/// Neutral authentication status summary. +/// Remote control is in the middle of initial setup. +/// The connecting variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthStatusDto +public partial class RemoteControlStatusConnecting : RemoteControlStatus { - /// Number of signed-in accounts in the roster. - [JsonPropertyName("accountCount")] - public long AccountCount { get; set; } - - /// Active account host, if authenticated. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("activeHost")] - public string? ActiveHost { get; set; } - - /// Active account login, if authenticated. - [JsonPropertyName("activeLogin")] - public string? ActiveLogin { get; set; } - - /// Copilot plan tier of the active account, if known. - [JsonPropertyName("copilotPlan")] - public string? CopilotPlan { get; set; } + /// + [JsonIgnore] + public override string State => "connecting"; - /// Whether the session has resolved authentication. - [JsonPropertyName("isAuthenticated")] - public bool IsAuthenticated { get; set; } + /// Session id the connection is attaching to. + [JsonPropertyName("attachedSessionId")] + public required string AttachedSessionId { get; set; } } -/// The status variant of . +/// Remote control is connected to a local session. +/// The active variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueStatus : AuthReadValue +public partial class RemoteControlStatusActive : RemoteControlStatus { /// [JsonIgnore] - public override string Kind => "status"; + public override string State => "active"; - /// The neutral authentication status summary. - [JsonPropertyName("status")] - public required AuthStatusDto Status { get; set; } + /// 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; } } -/// The lastErrors variant of . +/// The last setup attempt failed. The singleton is otherwise off. +/// The error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueLastErrors : AuthReadValue +public partial class RemoteControlStatusError : RemoteControlStatus { /// [JsonIgnore] - public override string Kind => "lastErrors"; + public override string State => "error"; - /// Validation errors from the most recent authentication attempt. - [JsonPropertyName("errors")] - public required IList Errors { get; set; } -} + /// Session id the failing setup attempt targeted, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("attachedSessionId")] + public string? AttachedSessionId { 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; + /// Human-readable error message from the last setup attempt. + [JsonPropertyName("error")] + public required string Error { get; set; } } - -/// The activeAccount variant of . +/// Wrapper for the singleton's current status. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryActiveAccount : AuthReadQuery +public sealed class RemoteControlStatusResult { - /// - [JsonIgnore] - public override string Kind => "activeAccount"; + /// State of the runtime-managed remote-control singleton. + [JsonPropertyName("status")] + public RemoteControlStatus Status { get => field ??= new(); set; } } -/// The status variant of . +/// Reattach to an existing MC session without creating a new one. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryStatus : AuthReadQuery +public sealed class RemoteControlConfigExistingMcSession { - /// - [JsonIgnore] - public override string Kind => "status"; + /// 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; } -/// The lastErrors variant of . +/// Configuration for the runtime-managed remote-control singleton. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryLastErrors : AuthReadQuery +public sealed class RemoteControlConfig { - /// - [JsonIgnore] - public override string Kind => "lastErrors"; + /// Reattach to an existing MC session without creating a new one. + [JsonPropertyName("existingMcSession")] + public RemoteControlConfigExistingMcSession? ExistingMcSession { 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; } + + /// Whether remote export should be enabled. + [JsonPropertyName("remote")] + public bool Remote { get; set; } + + /// When true, suppresses timeline messages on successful setup. + [JsonPropertyName("silent")] + public bool Silent { get; set; } + + /// Whether the MC session may steer the local session (write mode). + [JsonPropertyName("steerable")] + public bool Steerable { get; set; } + + /// Existing Mission Control task ID to attach the exported session to. + [JsonPropertyName("taskId")] + public string? TaskId { get; set; } } -/// Read request carrying the typed datum query. +/// Parameters for attaching the remote-control singleton to a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsGetRequest +internal sealed class SessionsStartRemoteControlRequest { - /// Which typed accounts datum to read. - [JsonPropertyName("query")] - public AuthReadQuery Query { 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; } -/// Result of a non-interactive accounts mutation. +/// Outcome of a transferRemoteControl call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthWriteResult +public sealed class RemoteControlTransferResult { - /// For a logout, whether other signed-in accounts remain. - [JsonPropertyName("moreUsers")] - public bool? MoreUsers { get; set; } + /// State of the runtime-managed remote-control singleton. + [JsonPropertyName("status")] + public RemoteControlStatus Status { get => field ??= new(); set; } - /// Whether the mutation was applied. - [JsonPropertyName("ok")] - public bool Ok { get; set; } + /// Whether the rebinding actually happened. + [JsonPropertyName("transferred")] + public bool Transferred { get; set; } } -/// One non-interactive accounts mutation command (the selector is fused with its typed args). -/// Polymorphic base type discriminated by kind. +/// Parameters for atomically rebinding the remote-control singleton. [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 SessionsTransferRemoteControlRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { 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; } -/// The switchActive variant of . + /// Local session id to point remote control at. + [JsonPropertyName("toSessionId")] + public string ToSessionId { get; set; } = string.Empty; +} + +/// Patch for the singleton's steering state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteSwitchActive : AuthWrite +internal sealed class SessionsSetRemoteControlSteeringRequest { - /// - [JsonIgnore] - public override string Kind => "switchActive"; - - /// Opaque selection id of the account to make active. - [JsonPropertyName("selectionId")] - public required string SelectionId { get; set; } + /// 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; } } -/// The logout variant of . +/// Outcome of a stopRemoteControl call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteLogout : AuthWrite +public sealed class RemoteControlStopResult { - /// - [JsonIgnore] - public override string Kind => "logout"; + /// State of the runtime-managed remote-control singleton. + [JsonPropertyName("status")] + public RemoteControlStatus Status { get => field ??= new(); set; } - /// 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; } + /// Whether the singleton was actually torn down by this call. + [JsonPropertyName("stopped")] + public bool Stopped { get; set; } } -/// The setCredentials variant of . +/// RPC data type for SessionsStopRemoteControl operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteSetCredentials : AuthWrite +internal sealed class SessionsStopRemoteControlRequest { - /// - [JsonIgnore] - public override string Kind => "setCredentials"; - - /// Authentication host URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public required string Host { get; set; } - - /// Login/username for the credential. - [JsonPropertyName("login")] - public required string Login { 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; } - /// GitHub authentication token to install. - [JsonPropertyName("token")] - public required string Token { 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; } } -/// Mutation request carrying the typed write command. +/// Params to attach or detach an in-process ExtensionController delegate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsSetRequest +internal sealed class ConfigureSessionExtensionsParams { - /// The non-interactive mutation command to apply. - [JsonPropertyName("command")] - public AuthWrite Command { get => field ??= new(); set; } - - /// Target session identifier. + /// Session to attach the extension controller delegate to. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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). +/// Outcome of an agentRegistry.spawn call. /// Polymorphic base type discriminated by kind. [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 +[JsonDerivedType(typeof(AgentRegistrySpawnResultSpawned), "spawned")] +[JsonDerivedType(typeof(AgentRegistrySpawnResultSpawnError), "spawn-error")] +[JsonDerivedType(typeof(AgentRegistrySpawnResultRegistryTimeout), "registry-timeout")] +[JsonDerivedType(typeof(AgentRegistrySpawnResultValidationError), "validation-error")] +public partial class AgentRegistrySpawnResult { /// The type discriminator. [JsonPropertyName("kind")] @@ -12047,5051 +11653,5083 @@ public partial class AuthLoginStep } -/// The open-url variant of . +/// 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)] -public partial class AuthLoginStepOpenUrl : AuthLoginStep +public sealed class AgentRegistryLiveTargetEntry { - /// - [JsonIgnore] - public override string Kind => "open-url"; + /// Kind of attention required when status === "attention". Meaningful only when status === "attention". + [JsonPropertyName("attentionKind")] + public AgentRegistryLiveTargetEntryAttentionKind? AttentionKind { get; set; } - /// Authorize URL the consumer should open in a browser (consumer-driven browser-open). - [JsonPropertyName("url")] - public required string Url { get; set; } -} + /// Git branch of the session (when known). + [JsonPropertyName("branch")] + public string? Branch { get; set; } -/// The input-required variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepInputRequired : AuthLoginStep -{ - /// - [JsonIgnore] - public override string Kind => "input-required"; + /// Copilot CLI version that wrote the entry. + [JsonPropertyName("copilotVersion")] + public string CopilotVersion { 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; } + /// Working directory of the session (when known). + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } + + /// Bind host for the entry's JSON-RPC server. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// Process kind tag for the registry entry. + [JsonPropertyName("kind")] + public AgentRegistryLiveTargetEntryKind Kind { get; set; } + + /// 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; } + + /// Model identifier currently selected for the session. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// Operating-system pid of the process owning this entry. + [JsonPropertyName("pid")] + public long Pid { get; set; } + + /// TCP port the entry's JSON-RPC server is listening on. + [JsonPropertyName("port")] + public long Port { get; set; } + + /// Registry entry schema version (1 = ui-server, 2 = managed-server). + [JsonPropertyName("schemaVersion")] + public long SchemaVersion { get; set; } + + /// Session ID of the foreground session for this entry. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } + + /// Friendly session name (when set). + [JsonPropertyName("sessionName")] + public string? SessionName { get; set; } + + /// ISO 8601 timestamp captured at registration. + [JsonPropertyName("startedAt")] + public string StartedAt { get; set; } = string.Empty; + + /// Coarse lifecycle status of the foreground session. + [JsonPropertyName("status")] + public AgentRegistryLiveTargetEntryStatus? Status { get; set; } + + /// Monotonic per-publisher revision counter incremented on every status update. Lets watchers detect transient flips. + [JsonPropertyName("statusRevision")] + public long? StatusRevision { get; set; } + + /// Connection token (null when the target is unauthenticated). + [JsonInclude] + [JsonPropertyName("token")] + internal string? Token { get; set; } } -/// The awaiting variant of . +/// Canonical process-log discovery outcome; populated from spawnLiveTarget. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepAwaiting : AuthLoginStep +public sealed class AgentRegistryLogCapture { - /// - [JsonIgnore] - public override string Kind => "awaiting"; + /// 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; } + + /// Categorized reason no canonical process log could be opened (set only when enabled is false). + [JsonPropertyName("openErrorReason")] + public AgentRegistryLogCaptureOpenErrorReason? OpenErrorReason { 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; } } -/// The needs-interaction variant of . +/// Managed-server child was spawned and registered successfully. +/// The spawned variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepNeedsInteraction : AuthLoginStep +public partial class AgentRegistrySpawnResultSpawned : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Kind => "needs-interaction"; -} + public override string Kind => "spawned"; -/// A credential-free account choice after sign-in. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginAccount -{ - /// Host coordinate owned by the selected account's provider. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// 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; } - /// Provider kind that owns this account choice. - [JsonPropertyName("kind")] - public AccountKind Kind { 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; } - /// Human-readable login for the account choice. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// 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; } - /// Opaque identifier supplied to the next login step to select this account. - [JsonPropertyName("selectionId")] - public string SelectionId { get; set; } = string.Empty; + /// Per-spawn log-capture outcome; populated from spawnLiveTarget. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logCapture")] + public AgentRegistryLogCapture? LogCapture { get; set; } } -/// Result of an interactive login flow. Pending consent or account selection is not terminal. +/// `child_process.spawn` itself failed before the child entered the registry. +/// The spawn-error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginResultDto +public partial class AgentRegistrySpawnResultSpawnError : AgentRegistrySpawnResult { - /// Available accounts when sign-in is awaiting account selection, ordered with Microsoft 365 first. - [JsonPropertyName("accounts")] - public IList? Accounts { get; set; } - - /// Host that was signed in, when completed. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public string? Host { get; set; } + /// + [JsonIgnore] + public override string Kind => "spawn-error"; - /// Login that was signed in, when completed. - [JsonPropertyName("login")] - public string? Login { get; set; } + /// Underlying errno code (e.g. ENOENT, EACCES) when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("code")] + public string? Code { get; set; } - /// Current disposition of the login, including pending user decisions. - [JsonPropertyName("status")] - public AuthLoginResultStatus Status { get; set; } + /// Human-readable error message. + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// The completed 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 AuthLoginStepCompleted : AuthLoginStep +public partial class AgentRegistrySpawnResultRegistryTimeout : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Kind => "completed"; + public override string Kind => "registry-timeout"; - /// 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; } + /// Process ID of the orphaned child (so the caller can offer 'kill the pid' guidance). + [JsonPropertyName("childPid")] + public required long ChildPid { get; set; } + + /// Per-spawn log-capture outcome; populated from spawnLiveTarget. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logCapture")] + public AgentRegistryLogCapture? LogCapture { get; set; } } -/// The error variant of . +/// Synchronous pre-validation rejected the spawn request. +/// The validation-error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepError : AuthLoginStep +public partial class AgentRegistrySpawnResultValidationError : AgentRegistrySpawnResult { /// [JsonIgnore] - public override string Kind => "error"; + public override string Kind => "validation-error"; - /// Human-readable failure message. + /// Which parameter field was invalid. Omitted when the rejection is not field-specific. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("field")] + public AgentRegistrySpawnValidationErrorField? Field { 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; } + + /// 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; } } -/// A started login flow: its opaque id and first step. +/// Inputs to spawn a managed-server child via the controller's spawn delegate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginBegun +internal sealed class AgentRegistrySpawnRequest { - /// Opaque flow id used to advance or cancel this login. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; + /// 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 first step of the flow. - [JsonPropertyName("step")] - public AuthLoginStep Step { get => field ??= new(); 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; } + + /// 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; } } -/// Begin an interactive login flow for a provider kind. Dispatch is kind-only. +/// Feature availability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginBeginRequest +public sealed class ConnectorDiscoveryCapabilities { - /// The provider kind to sign in with. - [JsonPropertyName("kind")] - public LoginProviderKind Kind { 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; } } -/// Advance an in-flight login flow, optionally fulfilling an input-required step. +/// Account metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginAdvanceRequest +public sealed class ConnectorDiscoveryAuthInfo { - /// Opaque flow id from begin. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; + /// Host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Neutral input fulfilling a preceding input-required step (e.g. a GHEC host); ignored otherwise. - [JsonPropertyName("input")] - public string? Input { get; set; } + /// Login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Authentication type. + [JsonPropertyName("type")] + public AuthInfoType Type { get; set; } } -/// Cancel an in-flight login flow. +/// Eligible account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginCancelRequest +public sealed class ConnectorDiscoveryAccount { - /// Opaque flow id from begin. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; + /// Opaque account ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Account metadata. + [JsonPropertyName("authInfo")] + public ConnectorDiscoveryAuthInfo AuthInfo { get => field ??= new(); set; } } -/// A file included in the session debug bundle. +/// Eligible accounts. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsCollectedEntry +public sealed class ConnectorDiscoveryAccountList { - /// Relative path of the file in the staged bundle/archive. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; - - /// Redacted output size in bytes. - [JsonPropertyName("sizeBytes")] - public long SizeBytes { get; set; } + /// Eligible accounts. + [JsonPropertyName("accounts")] + public IList Accounts { get => field ??= []; set; } - /// Source category for this entry. - [JsonPropertyName("source")] - public DebugCollectLogsSource Source { get; set; } + /// Availability. + [JsonPropertyName("availability")] + public ConnectorDiscoveryAvailability Availability { get; set; } } -/// An optional debug bundle entry that could not be included. +/// Entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsSkippedEntry +public sealed class ConnectorDiscoveryCatalogEntry { - /// Relative path requested for this bundle entry. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; + /// Description. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Server-local source path that could not be read. - [JsonPropertyName("path")] - public string? Path { get; set; } + /// Display name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Reason the entry was skipped. - [JsonPropertyName("reason")] - public string Reason { get; set; } = string.Empty; -} + /// Logo. + [JsonPropertyName("logo")] + public string? Logo { 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; } + /// Name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Destination kind that was written. - [JsonPropertyName("kind")] - public DebugCollectLogsResultKind Kind { get; set; } + /// Release tag. + [JsonPropertyName("releaseTag")] + public string? ReleaseTag { 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; + /// Status. + [JsonPropertyName("status")] + public ConnectorCatalogStatus Status { get; set; } - /// Optional files or directories that could not be included. - [JsonPropertyName("skippedEntries")] - public IList? SkippedEntries { get; set; } + /// Tier. + [JsonPropertyName("tier")] + public string? Tier { get; set; } } -/// A caller-provided server-local file or directory to include in the debug bundle. +/// Entries for the selected account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsEntry +public sealed class ConnectorDiscoveryCatalogResult { - /// Relative path to use inside the staged bundle/archive. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; - - /// Kind of source path to include. - [JsonPropertyName("kind")] - public DebugCollectLogsEntryKind Kind { get; set; } + /// Opaque account ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; - /// Server-local source path to read. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Entries. + [JsonPropertyName("connectors")] + public IList Connectors { get => field ??= []; 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; } + /// Refresh time in Unix epoch milliseconds. + [JsonPropertyName("refreshedAtMs")] + public long RefreshedAtMs { 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; } + /// Revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } } -/// Destination for the session debug bundle. -/// Polymorphic base type discriminated by kind. +/// Selected account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(DebugCollectLogsDestinationArchive), "archive")] -[JsonDerivedType(typeof(DebugCollectLogsDestinationDirectory), "directory")] -public partial class DebugCollectLogsDestination +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 SessionSuspendRequest +{ + /// Target session identifier. + [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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[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; } -/// The archive variant of . +/// 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 partial class DebugCollectLogsDestinationArchive : DebugCollectLogsDestination +public sealed class JsonSchemaResponseFormat { - /// - [JsonIgnore] - public override string Kind => "archive"; + /// Optional description passed to OpenAI providers. + [JsonPropertyName("description")] + public string? Description { get; 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; } + /// Name of the output schema, subject to the provider's naming restrictions. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Absolute or server-relative path for the .tgz archive to create. - [JsonPropertyName("outputPath")] - public required string OutputPath { get; set; } + /// 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 directory variant of . +/// The json_schema variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DebugCollectLogsDestinationDirectory : DebugCollectLogsDestination +public partial class ResponseFormatJsonSchema : ResponseFormat { /// [JsonIgnore] - public override string Kind => "directory"; + public override string Type => "json_schema"; - /// Directory where files should be staged. The directory is created if needed. - [JsonPropertyName("outputDirectory")] - public required string OutputDirectory { get; set; } + /// JSON Schema and provider options for the turn's output. + [JsonPropertyName("jsonSchema")] + public required JsonSchemaResponseFormat JsonSchema { get; set; } } -/// Built-in session diagnostics to include in the bundle. Omitted fields default to true. +/// Parameters for sending a user message to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsInclude +internal sealed class SendRequest { - /// 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; } + /// 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; } - /// Include the session event log (`events.jsonl`). Defaults to true. - [JsonPropertyName("events")] - public bool? Events { get; set; } + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the message. + [JsonPropertyName("attachments")] + public IList? Attachments { 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; } + /// If false, this message will not trigger a Premium Request Unit charge. User messages default to billable. + [JsonPropertyName("billable")] + public bool? Billable { get; set; } - /// Maximum number of previous process logs to include. Defaults to 5. - [JsonPropertyName("previousProcessLogLimit")] - public long? PreviousProcessLogLimit { get; set; } + /// If provided, this is shown in the timeline instead of `prompt`. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { 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; } + /// 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; } - /// Include process logs for the session. Defaults to true. - [JsonPropertyName("processLogs")] - public bool? ProcessLogs { get; set; } + /// If true, adds the message to the front of the queue instead of the end. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } - /// Include interactive shell logs written under the session's `shell-logs` directory. Defaults to true. - [JsonPropertyName("shellLogs")] - public bool? ShellLogs { get; set; } -} + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; -/// 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; } + /// 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; } - /// 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; } + /// 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; } - /// Which built-in session diagnostics to include. Omitted fields default to true. - [JsonPropertyName("include")] - public DebugCollectLogsInclude? Include { 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; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} -/// 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)] -public sealed class CanvasAction -{ - /// Description of the action. - [JsonPropertyName("description")] - public string? Description { 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; } - /// JSON Schema for the action input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { get; set; } + /// W3C Trace Context traceparent header for distributed tracing of this agent turn. + [JsonPropertyName("traceparent")] + public string? Traceparent { get; set; } - /// Action name exposed by the canvas provider. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } } -/// Canvas available in the current session. +/// Result of sending zero or more user messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredCanvas +public sealed class SendMessagesResult { - /// 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; + /// 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; } +} - /// Human-readable canvas name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; +/// A single user message to append to the session as part of a `session.sendMessages` turn. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SendMessageItem +{ + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with this message. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// 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; } - /// Owning extension display name, when available. - [JsonPropertyName("extensionName")] - public string? ExtensionName { get; set; } + /// If provided, this is shown in the timeline instead of `prompt`. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } - /// Host-local PNG path for the canvas icon, when supplied. - [JsonPropertyName("icon")] - public string? Icon { get; set; } + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// JSON Schema for canvas open input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { 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; } -/// 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; } + /// 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; } } -/// Identifies the target session. +/// 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)] -internal sealed class SessionCanvasListRequest +internal sealed class SendMessagesRequest { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// 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; } -/// Open canvas instance snapshot. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OpenCanvasInstance -{ - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; + /// 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; } - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// 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; } - /// Owning extension display name, when available. - [JsonPropertyName("extensionName")] - public string? ExtensionName { get; set; } + /// If true, adds the messages to the front of the queue instead of the end. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } - /// Host-local PNG path for the canvas icon, when supplied. - [JsonPropertyName("icon")] - public string? Icon { 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; } - /// Input supplied when the instance was opened. - [JsonPropertyName("input")] - public JsonElement? Input { 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; } - /// Stable caller-supplied canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Provider-supplied status text. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// W3C Trace Context traceparent header for distributed tracing of this agent turn. + [JsonPropertyName("traceparent")] + public string? Traceparent { get; set; } - /// Rendered title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// W3C Trace Context tracestate header for distributed tracing. + [JsonPropertyName("tracestate")] + public string? Tracestate { get; set; } - /// URL for web-rendered canvases. - [JsonPropertyName("url")] - public string? Url { 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; } } -/// Live open-canvas snapshot. +/// Internal request for sending a system notification. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasListOpenResult +internal sealed class SendSystemNotificationRequest { - /// Currently open canvas instances. - [JsonPropertyName("openCanvases")] - public IList OpenCanvases { get => field ??= []; set; } -} + /// 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; } -/// 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; } -/// Canvas open parameters. +/// Result of aborting the current turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasOpenRequest +public sealed class AbortResult { - /// 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; + /// Error message if the abort failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the abort completed successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Canvas close parameters. +/// Parameters for aborting the current turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasCloseRequest +internal sealed class AbortRequest { - /// Open canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// 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; } -/// Canvas action invocation result. +/// Result of interrupting the main agent turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasActionInvokeResult +public sealed class InterruptMainTurnResult { - /// Provider-supplied action result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Whether an in-flight main agent turn was interrupted. False when the main loop was not processing. + [JsonPropertyName("interrupted")] + public bool Interrupted { get; set; } } -/// Canvas action invocation parameters. +/// Parameters for interrupting the main agent turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasActionInvokeRequest +internal sealed class InterruptMainTurnRequest { - /// 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; + /// 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; } -/// Internal canvas provider registration parameters. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasProviderRegisterRequest +internal sealed class SessionCancelAllBackgroundAgentsRequest { - /// 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; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Internal canvas provider unregistration parameters. +/// Parameters for shutting down the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasProviderUnregisterRequest +internal sealed class ShutdownRequest { - /// Connection identifier to unregister. - [JsonPropertyName("connectionId")] - public string ConnectionId { 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; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Why the session is being shut down. Defaults to "routine" when omitted. + [JsonPropertyName("type")] + public ShutdownType? Type { get; set; } } -/// Machine-readable workflow run failure. -/// Polymorphic base type discriminated by type. +/// Identifier of the session event that was emitted for the log message. [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 +public sealed class LogResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// The unique identifier of the emitted session event. + [JsonPropertyName("eventId")] + public Guid EventId { get; set; } } - -/// The workflow_limit_reached 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 WorkflowRunFailureWorkflowLimitReached : WorkflowRunFailure +internal sealed class LogRequest { - /// - [JsonIgnore] - public override string Type => "workflow_limit_reached"; + /// When true, the message is transient and not persisted to the session event log on disk. + [JsonPropertyName("ephemeral")] + public bool? Ephemeral { get; set; } - /// Resource ceiling that stopped the run. - [JsonPropertyName("kind")] - public required WorkflowRunFailureKind Kind { get; set; } + /// Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". + [JsonPropertyName("level")] + public SessionLogLevel? Level { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// Human-readable message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Suggested larger ceiling when the runtime can derive one safely. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("suggestedValue")] - public double? SuggestedValue { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Approved effective ceiling that was reached. - [JsonPropertyName("value")] - public required double Value { get; set; } + /// 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; } } -/// The workflow_resume_declined variant of . +/// Adapter-declared policy that tells clients whether discovery may run automatically. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowResumeDeclined : WorkflowRunFailure +public sealed class ModelProviderAutomaticDiscoveryPolicy { - /// - [JsonIgnore] - public override string Type => "workflow_resume_declined"; - - /// Human-readable reason the resume did not proceed. - [JsonPropertyName("reason")] - public required string Reason { get; set; } + /// Whether automatic discovery is allowed, limited to configured providers, or explicit-only. + [JsonPropertyName("mode")] + public ModelProviderAutomaticDiscoveryMode Mode { get; set; } - /// Workflow run identifier whose changed limits were declined. - [JsonPropertyName("runId")] - public required string RunId { 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; } } -/// The workflow_durable_failure variant of . +/// An operation supported by a model-provider adapter. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowDurableFailure : WorkflowRunFailure +public sealed class ModelProviderAdapterOperationDescriptor { - /// - [JsonIgnore] - public override string Type => "workflow_durable_failure"; - - /// Stable failure code. - [JsonPropertyName("code")] - public required string Code { get; set; } - - /// Execution-critical durable operation that failed. - [JsonPropertyName("operation")] - public required WorkflowDurableOperation Operation { get; set; } + /// 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; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { 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; } -/// The run stopped because its usage accounting could not be completed. -/// The workflow_accounting_incomplete variant of . +/// Contributor attribution, independent of routing identity and authorization. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowAccountingIncomplete : WorkflowRunFailure +public sealed class ModelProviderAttribution { - /// - [JsonIgnore] - public override string Type => "workflow_accounting_incomplete"; + /// Human-readable contributor name, not the adapter display name. + [JsonPropertyName("ownerDisplayName")] + public string? OwnerDisplayName { get; set; } - /// Confirmed usage in nano-AIU, representing the floor of what the run spent. - [JsonPropertyName("drainedNanoAiu")] - public required long DrainedNanoAiu { 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; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// Kind of component that supplied the adapter. Attribution does not confer authority. + [JsonPropertyName("source")] + public ModelProviderProvenanceSource Source { get; set; } } -/// 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 . +/// A normalized model-provider adapter in the session's effective catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowProviderDisconnected : WorkflowRunFailure +public sealed class ModelProviderAdapterDescriptor { - /// - [JsonIgnore] - public override string Type => "workflow_provider_disconnected"; + /// 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; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { 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; } -/// Durable metadata describing who initiated a workflow pause. -/// Polymorphic base type discriminated by type. +/// Normalized model-provider adapter definitions available to the session, not discovered instances. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(WorkflowPauseInfoUser), "user")] -[JsonDerivedType(typeof(WorkflowPauseInfoCheckpoint), "checkpoint")] -public partial class WorkflowPauseInfo +public sealed class ModelProviderAdapterCatalog { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Available provider adapters ordered by adapterId. + [JsonPropertyName("providers")] + public IList Providers { get => field ??= []; set; } } - -/// The user variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowPauseInfoUser : WorkflowPauseInfo +internal sealed class SessionProvidersGetCatalogRequest { - /// - [JsonIgnore] - public override string Type => "user"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The checkpoint variant of . +/// Attribution for the adapter that produced a provider row. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowPauseInfoCheckpoint : WorkflowPauseInfo +public sealed class ModelProviderProvenance { - /// - [JsonIgnore] - public override string Type => "checkpoint"; + /// 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; - /// Stable author-defined checkpoint key that initiated the pause. - [JsonPropertyName("key")] - public required string Key { get; set; } + /// 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; } } -/// Complete current or terminal workflow run envelope. +/// Serializable reference to a discovered provider instance. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunResult +public sealed class ModelProviderInstanceReference { - /// 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; } + /// 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; - /// Error message for an errored run. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Stable instance identifier derived by the provider adapter, such as `ollama:{normalizedEndpoint}`. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Machine-readable failure details for a halted or errored run. - [JsonPropertyName("failure")] - public WorkflowRunFailure? Failure { get; set; } + /// 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; - /// Structured pause initiator metadata for a paused attempt. - [JsonPropertyName("pauseInfo")] - public WorkflowPauseInfo? PauseInfo { get; set; } + /// Descriptive provider family. Must match the selected adapter; not a routing key. + [JsonPropertyName("providerKind")] + public string ProviderKind { get; set; } = string.Empty; +} - /// Reason for a halted or cancelled run. - [JsonPropertyName("reason")] - public string? Reason { get; set; } +/// A normalized model-provider instance discovered by the runtime. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModelProviderInstance +{ + /// Human-readable instance name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Completed workflow result. - [JsonPropertyName("result")] - public JsonElement? 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; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Transport to use for inference against this instance. + [JsonPropertyName("inferenceTransport")] + public ProviderEndpointTransport? InferenceTransport { get; set; } - /// Partial journal and progress snapshot for a halted, cancelled, or errored run. - [JsonPropertyName("snapshot")] - public JsonElement? Snapshot { get; set; } + /// Provider family to use for inference against this instance. + [JsonPropertyName("inferenceType")] + public ProviderEndpointType? InferenceType { get; set; } - /// Current or terminal workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { 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; } } -/// Wire-only per-invocation workflow resource ceiling overrides. +/// Typed provider-operation outcome. Use the code for control flow and the optional message for display. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunLimits +public sealed class ModelProviderOperationOutcome { - /// 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; } + /// Machine-readable operation outcome. + [JsonPropertyName("code")] + public ModelProviderOperationOutcomeCode Code { get; set; } - /// Maximum number of workflow subagents that may run concurrently. - [JsonPropertyName("maxConcurrentSubagents")] - public long? MaxConcurrentSubagents { get; set; } + /// Human-readable detail for non-success outcomes. + [JsonPropertyName("message")] + public string? Message { get; set; } +} - /// Maximum total number of workflow subagents that may be admitted. - [JsonPropertyName("maxTotalSubagents")] - public long? MaxTotalSubagents { get; set; } +/// Provider instances found by a discovery operation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ModelProviderDiscoverResult +{ + /// Discovered provider instances. Empty when passive default discovery finds no reachable provider. + [JsonPropertyName("instances")] + public IList Instances { get => field ??= []; 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; } + /// Typed operation outcome. Passive discovery can return `absent` with an empty instance list. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } } -/// Options controlling workflow invocation. +/// Provider discovery parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunOptions +internal sealed class ModelProviderDiscoverRequest { - /// 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; } + /// Opaque adapter identity returned by `session.providers.getCatalog`. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; - /// Whether to notify the originating session when the workflow completes. - [JsonPropertyName("notifyOnComplete")] - public bool? NotifyOnComplete { get; 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; } - /// 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. +/// Current health information for a provider instance. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowRunRequest +public sealed class ModelProviderStatus { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } + /// Normalized provider instance. + [JsonPropertyName("instance")] + public ModelProviderInstance Instance { get => field ??= new(); set; } - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Typed operation outcome. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } - /// Workflow invocation options. - [JsonPropertyName("options")] - public WorkflowRunOptions? Options { get; set; } + /// Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provider-reported version. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Resolved persisted workflow identity and resumed run envelope. +/// Provider status request parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowResumeResult +internal sealed class ModelProviderGetStatusRequest { - /// Terminal resumed run envelope. - [JsonPropertyName("run")] - public WorkflowRunResult Run { get => field ??= new(); set; } + /// Provider instance reference returned by discovery. + [JsonPropertyName("instance")] + public ModelProviderInstanceReference Instance { get => field ??= new(); set; } - /// Persisted workflow name resolved for the resumed run. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for resuming a workflow run from its persisted identity. +/// Provider-reported model artifact metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowResumeRequest +public sealed class ModelArtifactDetails { - /// Optional per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// Provider-reported model architecture. + [JsonPropertyName("architecture")] + public string? Architecture { get; set; } - /// Whether to emit workflow phase names to the session transcript. - [JsonPropertyName("logPhaseNames")] - public bool? LogPhaseNames { get; set; } + /// Provider-reported model families. + [JsonPropertyName("families")] + public IList? Families { get; set; } - /// Whether to notify the originating session when the workflow completes. - [JsonPropertyName("notifyOnComplete")] - public bool? NotifyOnComplete { get; set; } + /// Primary model family. + [JsonPropertyName("family")] + public string? Family { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Artifact format, such as `gguf`. + [JsonPropertyName("format")] + public string? Format { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Options for an internal tool-originated workflow invocation. +/// A non-fatal provider observation warning. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowToolRunOptions +public sealed class ModelProviderWarning { - /// Per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// Machine-readable warning code. + [JsonPropertyName("code")] + public string Code { get; set; } = string.Empty; - /// Run identifier whose journal and progress should seed this resumed run. - [JsonPropertyName("resumeFromRunId")] - public string? ResumeFromRunId { get; set; } + /// Human-readable warning message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// Internal parameters for invoking a registered workflow from a tool. +/// 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)] -internal sealed class WorkflowToolRunRequest +public sealed class DiscoveredModel { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } + /// Provider-reported model capabilities. Omitted capability fields are unknown; explicit false values are preserved. + [JsonPropertyName("capabilities")] + public ModelCapabilities Capabilities { get => field ??= new(); set; } - /// Registered workflow name. + /// Provider-reported model artifact details. + [JsonPropertyName("details")] + public ModelArtifactDetails Details { get => field ??= new(); set; } + + /// Provider-reported artifact digest. + [JsonPropertyName("digest")] + public string? Digest { get; set; } + + /// Provider-native model identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Provider-reported last-modified timestamp. + [JsonPropertyName("modifiedAt")] + public DateTimeOffset? ModifiedAt { get; set; } + + /// Provider-reported display name. [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public string? Name { get; set; } - /// Tool-originated workflow invocation options. - [JsonPropertyName("options")] - public WorkflowToolRunOptions? Options { get; set; } + /// Attribution for the adapter that produced this model row. + [JsonPropertyName("provenance")] + public ModelProviderProvenance Provenance { get => field ??= new(); set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provider-reported artifact size in bytes. + [JsonPropertyName("sizeBytes")] + public long? SizeBytes { get; set; } - /// Opaque identifier of the originating tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } + /// Non-fatal warnings encountered while enriching this model. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Internal parameters for resuming a workflow run from a tool. +/// 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)] -internal sealed class WorkflowToolResumeRequest +public sealed class DiscoveredModelList { - /// Optional per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { 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; + /// Provider-native models in provider order. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; set; } - /// Opaque identifier of the originating tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } + /// Typed operation outcome. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } } -/// Parameters for retrieving a workflow run. +/// Provider model inventory request parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowGetRunRequest +internal sealed class ModelProviderModelsListRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { 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; } -/// Declared or approved workflow resource ceilings. +/// Vision-specific limits. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowDeclaredLimits +public sealed class ModelCapabilitiesOverrideLimitsVision { - /// Maximum AI credits consumed by subagents and descendants. - [JsonPropertyName("maxAiCredits")] - public double? MaxAiCredits { get; set; } - - /// Maximum concurrently active subagents. - [JsonPropertyName("maxConcurrentSubagents")] - public long? MaxConcurrentSubagents { get; set; } + /// Maximum image size in bytes. + [JsonPropertyName("max_prompt_image_size")] + public long? MaxPromptImageSize { get; set; } - /// Maximum total subagents spawned by the run. - [JsonPropertyName("maxTotalSubagents")] - public long? MaxTotalSubagents { get; set; } + /// Maximum number of images per prompt. + [JsonPropertyName("max_prompt_images")] + public long? MaxPromptImages { get; set; } - /// Maximum accumulated active execution time in seconds. - [JsonPropertyName("timeoutSeconds")] - public double? TimeoutSeconds { get; set; } + /// MIME types the model accepts. + [JsonPropertyName("supported_media_types")] + public IList? SupportedMediaTypes { get; set; } } -/// Durable workflow resource consumption. +/// Token limits for prompts, outputs, and context window. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunConsumed +public sealed class ModelCapabilitiesOverrideLimits { - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeMs")] - public long ActiveMs { get; set; } + /// Maximum total context window size in tokens. + [JsonPropertyName("max_context_window_tokens")] + public long? MaxContextWindowTokens { get; set; } - /// AI usage consumed by the run in nano-AIU. - [JsonPropertyName("nanoAiu")] - public long NanoAiu { get; set; } + /// Maximum number of output/completion tokens. + [JsonPropertyName("max_output_tokens")] + public long? MaxOutputTokens { get; set; } - /// Total subagents spawned by the run. - [JsonPropertyName("subagents")] - public long Subagents { get; set; } + /// Maximum number of prompt/input tokens. + [JsonPropertyName("max_prompt_tokens")] + public long? MaxPromptTokens { get; set; } + + /// Vision-specific limits. + [JsonPropertyName("vision")] + public ModelCapabilitiesOverrideLimitsVision? Vision { get; set; } } -/// Current workflow phase identity. +/// Feature flags indicating what the model supports. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowCurrentPhase +public sealed class ModelCapabilitiesOverrideSupports { - /// Current phase identifier. - [JsonPropertyName("id")] - public string Id { 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; } - /// Zero-based declared phase ordinal, or null for an undeclared phase. - [JsonPropertyName("ordinal")] - public long? Ordinal { get; set; } + /// Whether this model supports reasoning effort configuration. + [JsonPropertyName("reasoningEffort")] + public bool? ReasoningEffort { get; set; } + + /// Whether this model supports canonical tool calling. + [JsonPropertyName("toolCalls")] + public bool? ToolCalls { get; set; } + + /// Whether this model supports vision/image input. + [JsonPropertyName("vision")] + public bool? Vision { get; set; } } -/// Prompt-safe terminal workflow outcome. +/// Optional capability overrides (vision, tool_calls, reasoning, etc.). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunTerminal +public sealed class ModelCapabilitiesOverride { - /// Human-readable terminal error. - [JsonPropertyName("error")] - public string? Error { get; 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; } + /// Token limits for prompts, outputs, and context window. + [JsonPropertyName("limits")] + public ModelCapabilitiesOverrideLimits? Limits { get; set; } - /// Prompt-safe preview of the completed result. - [JsonPropertyName("resultPreview")] - public string? ResultPreview { get; set; } + /// Feature flags indicating what the model supports. + [JsonPropertyName("supports")] + public ModelCapabilitiesOverrideSupports? Supports { get; set; } } -/// Durable workflow run summary with read-time live overlays. +/// RPC data type for ProtocolSystemMessageAppendConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunSummary +public sealed class ProtocolSystemMessageAppendConfig { - /// Epoch milliseconds when the current active segment started, or null while inactive. - [JsonPropertyName("activeSegmentStartedAt")] - public long? ActiveSegmentStartedAt { get; set; } + /// Text appended to the standard system prompt. + [JsonPropertyName("content")] + public string? Content { get; set; } - /// Approved effective resource ceilings, or null until approved. - [JsonPropertyName("approved")] - public WorkflowDeclaredLimits? Approved { get; set; } + /// Append-mode discriminator. Omission also selects append mode. + [JsonPropertyName("mode")] + public ProtocolAppendMode? Mode { get; set; } +} - /// Whether the durable run state currently passes runtime resume eligibility checks. - [JsonPropertyName("canResume")] - public bool CanResume { get; set; } +/// RPC data type for SystemMessageBlock operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SystemMessageBlock +{ + /// Whether providers with explicit prompt caching should place a cache breakpoint after this block. + [JsonPropertyName("cacheBreakpoint")] + public bool? CacheBreakpoint { get; set; } - /// Epoch milliseconds when the run completed, or null while nonterminal. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// Text content for this system-message block. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Durable resource consumption. - [JsonPropertyName("consumed")] - public WorkflowRunConsumed Consumed { get => field ??= new(); set; } + /// Whether the block is static and may be cached independently of dynamic prompt content. + [JsonPropertyName("isStatic")] + public bool? IsStatic { get; set; } +} - /// Epoch milliseconds when the run was created. - [JsonPropertyName("createdAt")] - public long CreatedAt { get; set; } +/// RPC data type for ProtocolSystemMessageReplaceConfig operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProtocolSystemMessageReplaceConfig +{ + /// Complete replacement system-message text. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Current phase identity, or null before any phase is entered. - [JsonPropertyName("currentPhase")] - public WorkflowCurrentPhase? CurrentPhase { get; set; } + /// Optional structured blocks corresponding to the replacement content. + [JsonPropertyName("contentBlocks")] + public IList? ContentBlocks { get; set; } - /// Resource ceilings declared by the workflow. - [JsonPropertyName("declaredLimits")] - public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } + /// Replace-mode discriminator. + [JsonPropertyName("mode")] + public ProtocolReplaceMode Mode { get; set; } +} - /// Number of phases declared by the workflow. - [JsonPropertyName("declaredPhaseCount")] - public long DeclaredPhaseCount { get; set; } +/// RPC data type for ProtocolStaticSectionOverride operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProtocolStaticSectionOverride +{ + /// Declarative operation applied to the section. + [JsonPropertyName("action")] + public ProtocolStaticSectionAction Action { get; set; } - /// Human-readable workflow description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Optional content used by replace, append, and prepend operations. + [JsonPropertyName("content")] + public string? Content { get; set; } +} - /// Number of direct workflow agents currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } +/// Polymorphic base type discriminated by action. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "action", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(ProtocolMarkerSectionOverrideTransform), "transform")] +[JsonDerivedType(typeof(ProtocolMarkerSectionOverridePreserve), "preserve")] +public partial class ProtocolMarkerSectionOverride +{ + /// The type discriminator. + [JsonPropertyName("action")] + public virtual string Action { get; set; } = string.Empty; +} - /// Epoch milliseconds when this live-overlay snapshot was observed. - [JsonPropertyName("observedAt")] - public long ObservedAt { get; set; } - /// Monotonic durable run revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } +/// The transform variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ProtocolMarkerSectionOverrideTransform : ProtocolMarkerSectionOverride +{ + /// + [JsonIgnore] + public override string Action => "transform"; +} - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// The preserve variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class ProtocolMarkerSectionOverridePreserve : ProtocolMarkerSectionOverride +{ + /// + [JsonIgnore] + public override string Action => "preserve"; +} - /// Epoch milliseconds when execution first started, or null before start. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } +/// 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; } - /// Current workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { get; set; } + /// Gets the value when this instance contains . + public ProtocolMarkerSectionOverride? ProtocolMarkerSectionOverride { get; } - /// Terminal run outcome, or null while nonterminal. - [JsonPropertyName("terminal")] - public WorkflowRunTerminal? Terminal { get; set; } + /// Initializes a new instance of the class from . + public ProtocolSectionOverride(ProtocolStaticSectionOverride value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolStaticSectionOverride = value; + } - /// Total direct workflow agents spawned across all attempts. - [JsonPropertyName("totalSpawnedAgentCount")] - public long TotalSpawnedAgentCount { get; set; } + /// Converts to . + public static implicit operator ProtocolSectionOverride(ProtocolStaticSectionOverride value) => new(value); - /// Epoch milliseconds when the durable run was last updated. - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } + /// Initializes a new instance of the class from . + public ProtocolSectionOverride(ProtocolMarkerSectionOverride value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolMarkerSectionOverride = value; + } - /// Registered workflow name. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; -} + /// Converts to . + public static implicit operator ProtocolSectionOverride(ProtocolMarkerSectionOverride value) => new(value); -/// A page of workflow runs in durable creation order. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowListRunsResult -{ - /// Whether terminal runs newer than this page exist. - [JsonPropertyName("hasMoreNewer")] - public bool? HasMoreNewer { get; set; } + /// 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."); + } - /// Newest terminal-run cursor in this page, or null when the terminal window is empty. - [JsonPropertyName("newestSeq")] - public long? NewestSeq { 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); + } - /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. - [JsonPropertyName("oldestSeq")] - public long? OldestSeq { get; set; } + throw new JsonException("JSON value did not match any ProtocolSectionOverride variant."); + } - /// Number of terminal runs older than this page. - [JsonPropertyName("omittedOlder")] - public long? OmittedOlder { 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; + } - /// Workflow run summaries in durable creation order. - [JsonPropertyName("runs")] - public IList Runs { get => field ??= []; set; } + throw new JsonException("No ProtocolSectionOverride variant value is set."); + } + } } -/// Parameters for paging workflow runs. +/// RPC data type for ProtocolSystemMessageCustomizeConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowListRunsRequest +public sealed class ProtocolSystemMessageCustomizeConfig { - /// Exclusive forward cursor. - [JsonPropertyName("afterSeq")] - public long? AfterSeq { get; set; } - - /// Exclusive backward cursor. - [JsonPropertyName("beforeSeq")] - public long? BeforeSeq { get; set; } + /// Text appended after the customized sections. + [JsonPropertyName("content")] + public string? Content { get; set; } - /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. - [JsonPropertyName("limit")] - public int? Limit { get; set; } + /// Customize-mode discriminator. + [JsonPropertyName("mode")] + public ProtocolCustomizeMode Mode { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Named standard-prompt section overrides. + [JsonPropertyName("sections")] + public IDictionary? Sections { get; set; } } -/// Prompt-safe durable identity and live status for a direct workflow agent. +/// JSON union data type for ProtocolSystemMessageConfig. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentSummary +[JsonConverter(typeof(Converter))] +public sealed partial class ProtocolSystemMessageConfig { - /// Accumulated active agent time in milliseconds. - [JsonPropertyName("activeMs")] - public long ActiveMs { get; set; } + /// Gets the value when this instance contains . + public ProtocolSystemMessageAppendConfig? ProtocolSystemMessageAppendConfig { get; } - /// Prompt-safe live activity text. - [JsonPropertyName("activity")] - public string? Activity { get; set; } + /// Gets the value when this instance contains . + public ProtocolSystemMessageReplaceConfig? ProtocolSystemMessageReplaceConfig { get; } - /// Stable direct-agent identifier. - [JsonPropertyName("agentId")] - public string AgentId { get; set; } = string.Empty; + /// Gets the value when this instance contains . + public ProtocolSystemMessageCustomizeConfig? ProtocolSystemMessageCustomizeConfig { get; } - /// Registered agent type. - [JsonPropertyName("agentType")] - public string AgentType { get; set; } = string.Empty; + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageAppendConfig = value; + } - /// Epoch milliseconds when the agent completed. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) => new(value); - /// Friendly, non-unique name intended for display. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageReplaceConfig = value; + } - /// Friendly, non-unique name intended for display. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) => new(value); - /// Phase identifier active when the agent was launched, or null. - [JsonPropertyName("phaseId")] - public string? PhaseId { get; set; } + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageCustomizeConfig = value; + } - /// Model requested when the agent was launched. - [JsonPropertyName("requestedModel")] - public string? RequestedModel { get; set; } + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) => new(value); - /// Concrete model resolved for the agent. - [JsonPropertyName("resolvedModel")] - public string? ResolvedModel { get; set; } + /// 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."); + } - /// Owning workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + 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); + } - /// Epoch milliseconds when the agent started. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } + throw new JsonException("JSON value did not match any ProtocolSystemMessageConfig variant."); + } - /// Current durable or live agent status. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; + /// + 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; + } - /// Tool-call identifier that launched the agent. - [JsonPropertyName("toolCallId")] - public string ToolCallId { get; set; } = string.Empty; + throw new JsonException("No ProtocolSystemMessageConfig variant value is set."); + } + } } -/// Durable lifecycle and timing for one workflow phase. +/// A BYOK model definition referencing a named provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowPhaseObservation +public sealed class ProviderModelConfig { - /// 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; } - - /// Current live active time for this phase in milliseconds. - [JsonPropertyName("currentActiveMs")] - public long CurrentActiveMs { get; set; } - - /// Optional human-readable phase detail. - [JsonPropertyName("detail")] - public string? Detail { get; set; } - - /// Number of times execution entered this phase. - [JsonPropertyName("entryCount")] - public long EntryCount { get; set; } + /// Optional capability overrides (vision, tool_calls, reasoning, etc.). + [JsonPropertyName("capabilities")] + public ModelCapabilitiesOverride? Capabilities { get; set; } - /// Phase identifier. + /// 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; - /// Most recent run attempt that entered this phase, or `0` if the phase has never been entered. - [JsonPropertyName("lastEnteredRunAttempt")] - public long LastEnteredRunAttempt { get; set; } + /// Maximum context window tokens for the model. + [JsonPropertyName("maxContextWindowTokens")] + public double? MaxContextWindowTokens { get; set; } - /// Direct agents in this phase that are currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } + /// Maximum output tokens for the model. + [JsonPropertyName("maxOutputTokens")] + public double? MaxOutputTokens { get; set; } - /// Zero-based declared phase ordinal, or null for an undeclared phase. - [JsonPropertyName("ordinal")] - public long? Ordinal { get; set; } + /// Maximum prompt/input tokens for the model. + [JsonPropertyName("maxPromptTokens")] + public double? MaxPromptTokens { 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; } + /// Provider-published model metadata, preserved verbatim as the public Model.metadata object. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } - /// Derived lifecycle state of the phase. - [JsonPropertyName("status")] - public WorkflowPhaseStatus Status { get; set; } + /// Well-known base model id used for behavior/capability/config lookup. Defaults to `id`. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } - /// Human-readable phase title. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; + /// Display name for model pickers. Defaults to the provider-qualified selection id (`provider/id`). + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Total direct agents associated with this phase. - [JsonPropertyName("totalAgentCount")] - public long TotalAgentCount { get; set; } -} - -/// One durable workflow progress record. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowProgressLine -{ - /// Resume attempt that emitted this record. - [JsonPropertyName("attempt")] - public long Attempt { 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; } + /// Name of the configured provider that serves this model. + [JsonPropertyName("provider")] + public string Provider { get; set; } = string.Empty; - /// Global monotonic sequence number within the run. - [JsonPropertyName("seq")] - public long Seq { get; set; } + /// 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; } - /// Prompt-safe progress text. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// The model name sent to the provider API for inference. Defaults to `id`. + [JsonPropertyName("wireModel")] + public string? WireModel { get; set; } } -/// A bidirectional page of workflow progress. +/// Azure-specific provider options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowProgressPage +public sealed class ProviderConfigAzure { - /// 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; } - - /// Run revision reflected by this page. - [JsonPropertyName("revision")] - public long Revision { 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; } } -/// Full workflow run observability detail. +/// 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 WorkflowRunDetail +public sealed class NamedProviderConfig { - /// Epoch milliseconds when the current active segment started, or null while inactive. - [JsonPropertyName("activeSegmentStartedAt")] - public long? ActiveSegmentStartedAt { 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; } + /// Static API key used to authenticate provider requests. + [JsonPropertyName("apiKey")] + public string? ApiKey { get; set; } - /// Lifecycle and timing observations for each workflow phase. - [JsonPropertyName("phases")] - public IList Phases { get => field ??= []; set; } + /// Azure authentication configuration for the provider. + [JsonPropertyName("azure")] + public ProviderConfigAzure? Azure { get; set; } - /// Bidirectional page of durable workflow progress. - [JsonPropertyName("progress")] - public WorkflowProgressPage Progress { get => field ??= new(); set; } + /// Base URL for provider API requests. + [JsonPropertyName("baseUrl")] + public string BaseUrl { get; set; } = string.Empty; - /// Monotonic durable run revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } + /// Static bearer token used to authenticate provider requests. + [JsonPropertyName("bearerToken")] + public string? BearerToken { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Whether the host supplies bearer tokens dynamically. + [JsonPropertyName("hasBearerTokenProvider")] + public bool? HasBearerTokenProvider { get; set; } - /// Epoch milliseconds when execution first started, or null before start. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } + /// Additional HTTP headers included with provider requests. + [JsonPropertyName("headers")] + public IDictionary? Headers { get; set; } - /// Current workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { 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; } - /// Terminal run outcome, or null while nonterminal. - [JsonPropertyName("terminal")] - public WorkflowRunTerminal? Terminal { get; set; } + /// Unique provider name used to qualify model selection IDs. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Total direct workflow agents spawned across all attempts. - [JsonPropertyName("totalSpawnedAgentCount")] - public long TotalSpawnedAgentCount { get; set; } + /// Transport used to communicate with the provider. + [JsonPropertyName("transport")] + public ProviderConfigTransport? Transport { get; set; } - /// Epoch milliseconds when the durable run was last updated. - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } + /// Provider protocol family. + [JsonPropertyName("type")] + public ProviderConfigType? Type { get; set; } - /// Registered workflow name. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + /// Wire API used to communicate with the provider. + [JsonPropertyName("wireApi")] + public ProviderConfigWireApi? WireApi { get; set; } } -/// Parameters for paging workflow progress. +/// 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)] -internal sealed class WorkflowGetRunProgressRequest +public sealed class ModelProviderConfigurationPlan { - /// Exclusive forward cursor. - [JsonPropertyName("afterSeq")] - public long? AfterSeq { 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; } - /// Exclusive backward cursor. - [JsonPropertyName("beforeSeq")] - public long? BeforeSeq { 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 records to return. Defaults to 200 and is capped at 500. - [JsonPropertyName("limit")] - public int? Limit { 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; } - /// Optional phase identifier used to scope records and cursors. - [JsonPropertyName("phaseId")] - public string? PhaseId { 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; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Provider-qualified selection id (`provider/id`) to pass to `switchTo` once the plan is applied. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } } -/// Parameters for cancelling a workflow run. +/// 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)] -internal sealed class WorkflowCancelRequest +internal sealed class ModelProviderPrepareConfigurationRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// 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; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Parameters for pausing a running workflow. +/// Managed sandbox enforcement state for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowPauseRequest +public sealed class SandboxEnforcementStatus { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Whether an enforcement failure has permanently blocked the session. + [JsonPropertyName("blocked")] + public bool Blocked { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// The first sandbox enforcement failure that blocked the session. + [JsonPropertyName("reason")] + public string? Reason { get; set; } -/// RPC data type for SessionWorkflowPauseAtCheckpoint operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkflowPauseAtCheckpointResult -{ - /// Whether this execution attempt must pause or may continue. - [JsonPropertyName("action")] - public WorkflowPauseCheckpointAction Action { get; set; } + /// Whether the effective managed policy requires an available sandbox backend. + [JsonPropertyName("required")] + public bool Required { get; set; } } -/// Parameters for an owned durable pause checkpoint. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowPauseCheckpointRequest +internal sealed class SessionSandboxGetEnforcementStatusRequest { - /// 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; } -/// Acknowledgement that a workflow request was accepted. +/// 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 WorkflowAckResult +public sealed class SandboxCredentialSuggestion { + /// Environment variable name. The secret value is never returned. + [JsonPropertyName("name")] + public string Name { 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; } } -/// One ordered workflow progress line. +/// Possible unconfigured secrets in the sandbox shell environment, sorted by variable name, with no secret values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowLogLine +public sealed class SandboxCredentialSuggestionsResult { - /// Progress line kind. - [JsonPropertyName("kind")] - public WorkflowLogLineKind Kind { 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; + /// Candidates for user review. Empty when sandboxing is disabled or no unconfigured candidates are present. + [JsonPropertyName("suggestions")] + public IList Suggestions { get => field ??= []; set; } } -/// Parameters for recording workflow progress. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowLogRequest +internal sealed class SessionSandboxGetCredentialSuggestionsRequest { - /// 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; } -/// Result of one workflow-scoped subagent call. +/// Result of attempting to disable sandboxing for the current session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentResult +public sealed class SandboxDisableForSessionResult { - /// Agent result, omitted when the agent produced no result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// The authoritative sandbox enabled state after the operation. + [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; } } -/// Options for one workflow-scoped subagent call. +/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentOptions +public sealed class PermissionDecisionContext { - /// 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; } + /// Disposition of the permission request as observed by the responding client. + [JsonPropertyName("outcome")] + public PermissionDecisionOutcome Outcome { get; set; } - /// Optional model identifier for the subagent. - [JsonPropertyName("model")] - public string? Model { 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; } - /// Optional reasoning effort override for the subagent. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// Controlled reason or actor responsible for the response. + [JsonPropertyName("source")] + public PermissionDecisionSource Source { get; set; } - /// Optional JSON Schema for structured agent output. - [JsonPropertyName("schema")] - public JsonElement? Schema { get; set; } + /// Client surface that submitted the response. + [JsonPropertyName("surface")] + public PermissionDecisionSurface Surface { get; set; } } -/// Parameters for one workflow-scoped subagent call. +/// 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 WorkflowAgentRequest +internal sealed class SandboxDisableForSessionRequest { - /// 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; } + /// Optional attribution for the permission decision. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } - /// Prompt to send to the subagent. - [JsonPropertyName("prompt")] - public string Prompt { 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; - - /// Workflow run identifier that owns the subagent. - [JsonPropertyName("workflowRunId")] - public string WorkflowRunId { get; set; } = string.Empty; } -/// Result of reading a workflow journal entry. +/// Result of accepting a sandbox path grant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowJournalGetResult +public sealed class SandboxGrantPathForRequestResult { - /// Whether the journal contained the requested key. - [JsonPropertyName("hit")] - public bool Hit { get; set; } - - /// Cached JSON result. The hit field distinguishes a cached JSON null from a miss. - [JsonPropertyName("resultJson")] - public JsonElement? ResultJson { get; set; } + /// 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 reading a workflow journal entry. +/// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowJournalGetRequest +internal sealed class SandboxGrantPathForRequestRequest { - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; - - /// Namespaced journal key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// 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 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; } -/// Parameters for storing a workflow journal entry. +/// Authentication status and account metadata for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowJournalPutRequest +public sealed class SessionAuthStatus { - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Authentication type. + [JsonPropertyName("authType")] + public AuthInfoType? AuthType { get; set; } - /// Namespaced journal key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Copilot plan tier (e.g., individual_pro, business). + [JsonPropertyName("copilotPlan")] + public string? CopilotPlan { get; set; } - /// JSON result to memoize. - [JsonPropertyName("resultJson")] - public JsonElement ResultJson { get; set; } + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string? Host { 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; } + + /// 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; } -/// 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. +/// Indicates whether the credential update succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CurrentModel +public sealed class SessionSetCredentialsResult { - /// 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. 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; } - - /// Context tier for models that support multiple context-window sizes. - [JsonPropertyName("contextTier")] - public ContextTier? ContextTier { get; set; } - - /// Currently active model identifier. - [JsonPropertyName("modelId")] - public string? ModelId { get; 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; } + /// 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; } - /// 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; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Provider of planBaseModelId, when that saved selection is provider-qualified. - [JsonPropertyName("planBaseProviderId")] - public string? PlanBaseProviderId { 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; } - - /// 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. +/// 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)] -internal sealed class SessionModelGetCurrentRequest +[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 { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// RPC data type for ModelSwitchConfirmation operations. + +/// 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 sealed class ModelSwitchConfirmation +public partial class SettableAuthInfoHmac : SettableAuthInfo { - /// Current conversation token count before switching models. - [JsonPropertyName("currentTokens")] - public double CurrentTokens { get; set; } + /// + [JsonIgnore] + public override string Type => "hmac"; - /// Target model token limit used by the compaction preflight. - [JsonPropertyName("targetLimit")] - public double TargetLimit { 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; } - /// Display name of the model that requires compaction confirmation. - [JsonPropertyName("targetModelDisplayName")] - public string TargetModelDisplayName { get; set; } = string.Empty; + /// 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 model identifier active on the session after the switch. +/// 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 ModelSwitchToResult +public partial class SettableAuthInfoEnv : SettableAuthInfo { - /// Compaction confirmation projection when status is confirmation_required. - [JsonPropertyName("confirmation")] - public ModelSwitchConfirmation? Confirmation { 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; } - - /// Deprecation warnings associated with the selected model or options. - [JsonPropertyName("deprecationWarnings")] - public IList? DeprecationWarnings { get; set; } - - /// User-facing outcome message for the model switch. - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Currently active model identifier after the switch. - [JsonPropertyName("modelId")] - public string? ModelId { 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; } + /// + [JsonIgnore] + public override string Type => "env"; - /// Persistence failure encountered after applying the model switch. - [JsonPropertyName("persistenceError")] - public string? PersistenceError { 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; } - /// 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; } + /// Name of the environment variable the token was sourced from. + [JsonPropertyName("envVar")] + public required string EnvVar { get; set; } - /// Lifecycle result for the requested switch. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// Authentication host (e.g. https://github.com or a GHES host). + [JsonPropertyName("host")] + public required string Host { get; set; } - /// User-facing warning produced while applying the model switch. - [JsonPropertyName("warning")] - public string? Warning { 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; } -/// Environment variables consulted while resolving model-picker settings. -public sealed class ModelPickerSettingsContextEnvironment -{ + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// Filesystem and environment context used to resolve model-picker settings. +/// Token authentication accepted by session.gitHubAuth.setCredentials. +/// The token variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelPickerSettingsContext +public partial class SettableAuthInfoToken : SettableAuthInfo { - /// Optional Copilot configuration directory containing persisted settings. - [JsonPropertyName("configDir")] - public string? ConfigDir { get; set; } + /// + [JsonIgnore] + public override string Type => "token"; - /// Environment variables consulted while resolving model-picker settings. - [JsonPropertyName("environment")] - public ModelPickerSettingsContextEnvironment Environment { 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; } - /// User home directory used when resolving persisted settings. - [JsonPropertyName("homeDirectory")] - public string HomeDirectory { get; set; } = string.Empty; + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// RPC data type for ModelPickerPersistence operations. +/// 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 sealed class ModelPickerPersistenceRequest +public partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo { - /// Whether context tier was explicitly selected and should be persisted. - [JsonPropertyName("contextTierExplicit")] - public bool? ContextTierExplicit { get; set; } + /// + [JsonIgnore] + public override string Type => "copilot-api-token"; - /// Whether reasoning effort was explicitly selected and should be persisted. - [JsonPropertyName("reasoningEffortExplicit")] - public bool? ReasoningEffortExplicit { 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; } - /// Filesystem and environment context used to resolve settings persistence. - [JsonPropertyName("settingsContext")] - public ModelPickerSettingsContext SettingsContext { get => field ??= new(); set; } + /// Authentication host (always the public GitHub host). + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. +/// 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 sealed class ModelSwitchToRequest +public partial class SettableAuthInfoUser : SettableAuthInfo { - /// 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; } - - /// 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 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; } + /// + [JsonIgnore] + public override string Type => "user"; - /// When true, evaluate context-window compaction policy before applying the switch. - [JsonPropertyName("runCompactionPreflight")] - public bool? RunCompactionPreflight { 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; } - /// 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; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Output verbosity level to request for supported models. - [JsonPropertyName("verbosity")] - public Verbosity? Verbosity { get; set; } + /// OAuth user login. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. +/// An interactive account whose model provider owns its credentials. It carries no GitHub credential. +/// The account variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSwitchToRequestWithSession +public partial class SettableAuthInfoAccount : SettableAuthInfo { - /// 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; } + /// + [JsonIgnore] + public override string Type => "account"; - /// 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; } + /// Host coordinate owned by the account's model provider. + [JsonPropertyName("host")] + public required string Host { 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; } + /// Login identifying the provider-owned account. + [JsonPropertyName("login")] + public required string Login { get; set; } +} - /// Override individual model capabilities resolved by the runtime. - [JsonPropertyName("modelCapabilities")] - public ModelCapabilitiesOverride? ModelCapabilities { get; set; } +/// 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 SettableAuthInfoGhCli : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "gh-cli"; - /// Settings scope used when persisting the selected model. - [JsonPropertyName("modelChangeScope")] - public string? ModelChangeScope { 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; } - /// 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; + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Optional settings context and explicit-override flags used to persist a picker selection. - [JsonPropertyName("pickerPersistence")] - public ModelPickerPersistenceRequest? PickerPersistence { get; set; } + /// User login as reported by `gh auth status`. + [JsonPropertyName("login")] + public required string Login { 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; } + /// The token returned by `gh auth token`. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { 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; } +/// 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 partial class SettableAuthInfoApiKey : SettableAuthInfo +{ + /// + [JsonIgnore] + public override string Type => "api-key"; - /// Reasoning summary mode to request for supported model clients. - [JsonPropertyName("reasoningSummary")] - public ReasoningSummary? ReasoningSummary { get; set; } + /// The API key. Treat as a secret. + [JsonPropertyName("apiKey")] + public required string ApiKey { get; set; } - /// Optional repository settings scope to persist after the switch commits. - [JsonPropertyName("repoScope")] - public string? RepoScope { 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; } - /// Require the target to be currently available and enabled before applying the switch. - [JsonPropertyName("requireAvailable")] - public bool? RequireAvailable { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } +} - /// When true, evaluate context-window compaction policy before applying the switch. - [JsonPropertyName("runCompactionPreflight")] - public bool? RunCompactionPreflight { 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; - - /// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchAutoTierResult +internal sealed class SessionGitHubAuthGetCurrentAuthInfoRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSwitchAutoTierRequest +internal sealed class SessionGitHubAuthGetAllAuthAvailableRequest { - /// 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; - - /// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelApplyStartupOverlayRequest +internal sealed class SessionGitHubAuthRefreshCopilotUserRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Provider owning the repository-selected model. Omitted repository provider identity retains legacy bare-model behavior. - [JsonPropertyName("repoModelProviderId")] - public string? RepoModelProviderId { get; set; } +/// 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; - /// Reasoning effort selected by repository settings, when configured. - [JsonPropertyName("repoReasoningEffort")] - public string? RepoReasoningEffort { get; set; } + /// GitHub login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Model required by server-managed policy, when configured. - [JsonPropertyName("serverManagedModel")] - public string? ServerManagedModel { get; set; } + /// Whether to persist the token after login. + [JsonPropertyName("persist")] + public bool? Persist { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// GitHub authentication token. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// RPC data type for SessionModelClearStartupSeed operations. +/// Parameters for switching the session's active authentication. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelClearStartupSeedResult +internal sealed class SessionAuthSwitchRequest { - /// Gets or sets the cleared value. - [JsonPropertyName("cleared")] - public bool Cleared { get; set; } + /// Authentication information to activate. + [JsonPropertyName("authInfo")] + public AuthInfo AuthInfo { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Optional token paired with the authentication information. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// RPC data type for SessionModelClearStartupSeed operations. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelClearStartupSeedRequest +internal sealed class SessionGitHubAuthLogoutRequest { - /// 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; } -/// The applied host allowlist and effective session model policy after intersection. +/// Parameters identifying a GitHub authentication to log out. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSetAllowedModelsResult +internal sealed class SessionAuthLogoutUserRequest { - /// 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; } -} - -/// 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 ModelSetAllowedModelsRequest -{ - /// Exact model IDs to permit, or null to clear the host restriction. - [JsonPropertyName("allowedModels")] - public IList? AllowedModels { get; set; } + /// 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; } -/// 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. +/// Validation error from an authentication attempt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSetReasoningEffortResult +public sealed class AuthValidationError { - /// Reasoning effort level recorded on the session after the update. - [JsonPropertyName("reasoningEffort")] - public string ReasoningEffort { get; set; } = string.Empty; + /// 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; } -/// Reasoning effort level to apply to the currently selected model. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSetReasoningEffortRequest +internal sealed class SessionGitHubAuthLastAuthErrorsRequest { - /// 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; } -/// Availability of a server-advertised routing preference. +/// The enumerated collection, keyed by the same selector as the query. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutoTierStatus +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthEnumerateValueAccounts), "accounts")] +[JsonDerivedType(typeof(AuthEnumerateValueProviders), "providers")] +public partial class AuthEnumerateValue { - /// 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; } - - /// Extensible machine-readable unavailability reason. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// A server-advertised routing preference. Identifiers and execution types are extensible. + +/// One signed-in account in the roster forest. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutoTierDescriptor +public sealed class AccountStatus { - /// 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; + /// Whether this is the active account. + [JsonPropertyName("active")] + public bool Active { get; set; } - /// Opaque routing identifier transmitted unchanged to the provider. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Human-readable credential source used to authenticate this account, such as `gh` or `GITHUB_TOKEN`. + [JsonPropertyName("authSource")] + public string? AuthSource { get; set; } - /// Current account-specific availability. - [JsonPropertyName("status")] - public AutoTierStatus Status { get => field ??= new(); 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; } - /// Execution kind; this client supports `auto` preferences on the Auto model. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; -} + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; -/// 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; + /// The provider kind of this account. + [JsonPropertyName("kind")] + public AccountKind Kind { get; set; } - /// Provider that supplied this metadata, when the catalog is provider-attributed. - [JsonPropertyName("providerId")] - public string? ProviderId { get; set; } + /// Authenticated login/username. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Routing preferences in the server's presentation order. - [JsonPropertyName("tiers")] - public IList Tiers { get => field ??= []; set; } + /// Opaque selection id used to switch to, or log out, this account. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; } -/// Cost-category metadata for a CAPI model. +/// The accounts variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelPriceCategory +public partial class AuthEnumerateValueAccounts : AuthEnumerateValue { - /// CAPI model identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "accounts"; - /// Cost category assigned to the model. - [JsonPropertyName("priceCategory")] - public ModelPickerPriceCategory PriceCategory { get; set; } + /// The signed-in account forest; empty when not logged in. + [JsonPropertyName("items")] + public required IList Items { 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. +/// A provider offered for interactive login. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderDescriptor +public sealed class ProviderDescriptor { - /// Opaque, stable provider id, stamped onto every model this provider returns. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Whether this provider is currently available to sign in with. + [JsonPropertyName("available")] + public bool Available { get; set; } /// The neutral provider kind. [JsonPropertyName("kind")] - public ModelProviderKind Kind { get; set; } + public LoginProviderKind 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; - - /// Stable ordering key for presenting providers in a deterministic sequence. - [JsonPropertyName("ordering")] - public long Ordering { get; set; } } -/// The list of models available to this session. +/// The providers variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelList +public partial class AuthEnumerateValueProviders : AuthEnumerateValue { - /// Ordered Auto routing preferences discovered for this session's account. - [JsonPropertyName("auto")] - public AutoTierMetadata? Auto { 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; } - - /// 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; } + /// + [JsonIgnore] + public override string Kind => "providers"; - /// 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; } + /// The providers offered for interactive login. + [JsonPropertyName("items")] + public required IList Items { get; set; } +} - /// Per-quota snapshots returned alongside the model list, keyed by quota type. - [JsonPropertyName("quotaSnapshots")] - public IDictionary? QuotaSnapshots { 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; } -/// RPC data type for SessionModelList operations. + +/// The accounts variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelListRequest +public partial class AuthEnumerateQueryAccounts : AuthEnumerateQuery { - /// If true, bypasses the per-session model list cache and re-fetches from CAPI. - [JsonPropertyName("skipCache")] - public bool? SkipCache { get; set; } + /// + [JsonIgnore] + public override string Kind => "accounts"; } -/// RPC data type for SessionModelListRequestWithSession operations. +/// The providers variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelListRequestWithSession +public partial class AuthEnumerateQueryProviders : AuthEnumerateQuery { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "providers"; - /// If true, bypasses the per-session model list cache and re-fetches from CAPI. - [JsonPropertyName("skipCache")] - public bool? SkipCache { 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; } } -/// Identifies the target session. +/// Enumerate request carrying the typed collection query. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModeGetRequest +internal sealed class AccountsEnumerateRequest { + /// Which typed accounts collection to enumerate. + [JsonPropertyName("query")] + public AuthEnumerateQuery Query { get => field ??= new(); 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. +/// The read result, keyed by the same selector as the query. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModeSetResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthReadValueActiveAccount), "activeAccount")] +[JsonDerivedType(typeof(AuthReadValueStatus), "status")] +[JsonDerivedType(typeof(AuthReadValueLastErrors), "lastErrors")] +public partial class AuthReadValue { - /// Whether the host should arm an interactive continuation after the mode change. - [JsonPropertyName("armInteractiveContinuation")] - public bool? ArmInteractiveContinuation { get; 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; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } +/// The activeAccount variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadValueActiveAccount : AuthReadValue +{ + /// + [JsonIgnore] + public override string Kind => "activeAccount"; - /// Lifecycle status of the requested mode change. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; + /// The active account, or absent when not logged in. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("account")] + public AccountStatus? Account { get; set; } - /// User-facing warning produced while applying the mode change. - [JsonPropertyName("warning")] - public string? Warning { 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; } } -/// Agent interaction mode to apply to the session. +/// Neutral authentication status summary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModeSetRequest +public sealed class AuthStatusDto { - /// 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; } + /// Number of signed-in accounts in the roster. + [JsonPropertyName("accountCount")] + public long AccountCount { get; set; } - /// Provider owning planModel. Omit for legacy bare-model selection. - [JsonPropertyName("planModelProviderId")] - public string? PlanModelProviderId { get; set; } + /// Active account host, if authenticated. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("activeHost")] + public string? ActiveHost { get; set; } - /// Reasoning effort to use with the dedicated plan model. - [JsonPropertyName("planReasoningEffort")] - public string? PlanReasoningEffort { get; set; } + /// Active account login, if authenticated. + [JsonPropertyName("activeLogin")] + public string? ActiveLogin { get; set; } - /// Whether leaving plan mode should restore the session's previous model. - [JsonPropertyName("restorePlanModel")] - public bool? RestorePlanModel { get; set; } + /// Copilot plan tier of the active account, if known. + [JsonPropertyName("copilotPlan")] + public string? CopilotPlan { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the session has resolved authentication. + [JsonPropertyName("isAuthenticated")] + public bool IsAuthenticated { get; set; } } -/// The session's friendly name, or null when not yet set. +/// The status variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NameGetResult +public partial class AuthReadValueStatus : AuthReadValue { - /// The session name (user-set or auto-generated), or null if not yet set. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// + [JsonIgnore] + public override string Kind => "status"; + + /// The neutral authentication status summary. + [JsonPropertyName("status")] + public required AuthStatusDto Status { get; set; } } -/// Identifies the target session. +/// The lastErrors variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionNameGetRequest +public partial class AuthReadValueLastErrors : AuthReadValue { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "lastErrors"; + + /// Validation errors from the most recent authentication attempt. + [JsonPropertyName("errors")] + public required IList Errors { get; set; } } -/// New friendly name to apply to the session. +/// Selects which typed accounts datum to read. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class NameSetRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthReadQueryActiveAccount), "activeAccount")] +[JsonDerivedType(typeof(AuthReadQueryStatus), "status")] +[JsonDerivedType(typeof(AuthReadQueryLastErrors), "lastErrors")] +public partial class AuthReadQuery { - /// 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; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Indicates whether the auto-generated summary was applied as the session's name. + +/// The activeAccount variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NameSetAutoResult +public partial class AuthReadQueryActiveAccount : AuthReadQuery { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "activeAccount"; } -/// Auto-generated session summary to apply as the session's name when no user-set name exists. +/// The status variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class NameSetAutoRequest +public partial class AuthReadQueryStatus : AuthReadQuery { - /// 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; + /// + [JsonIgnore] + public override string Kind => "status"; } -/// Existence, contents, and resolved path of the session plan file. +/// The lastErrors variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadResult +public partial class AuthReadQueryLastErrors : AuthReadQuery { - /// 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; } - - /// Absolute file path of the plan file, or null if workspace is not enabled. - [JsonPropertyName("path")] - public string? Path { get; set; } + /// + [JsonIgnore] + public override string Kind => "lastErrors"; } -/// Identifies the target session. +/// Read request carrying the typed datum query. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadRequest +internal sealed class AccountsGetRequest { + /// 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; } -/// Replacement contents to write to the session plan file. +/// Result of a non-interactive accounts mutation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PlanUpdateRequest +public sealed class AuthWriteResult { - /// The new content for the plan file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// For a logout, whether other signed-in accounts remain. + [JsonPropertyName("moreUsers")] + public bool? MoreUsers { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the mutation was applied. + [JsonPropertyName("ok")] + public bool Ok { get; set; } } -/// Identifies the target session. +/// 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 SessionPlanDeleteRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthWriteSwitchActive), "switchActive")] +[JsonDerivedType(typeof(AuthWriteLogout), "logout")] +[JsonDerivedType(typeof(AuthWriteSetCredentials), "setCredentials")] +public partial class AuthWrite { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { 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. + +/// The switchActive variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanSqlTodosRow +public partial class AuthWriteSwitchActive : AuthWrite { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "switchActive"; - /// Todo identifier. - [JsonPropertyName("id")] - public string? Id { get; set; } + /// Opaque selection id of the account to make active. + [JsonPropertyName("selectionId")] + public required string SelectionId { get; set; } +} - /// Todo status. - [JsonPropertyName("status")] - public string? Status { get; set; } +/// The logout variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthWriteLogout : AuthWrite +{ + /// + [JsonIgnore] + public override string Kind => "logout"; - /// Todo title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// 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; } } -/// Todo rows read from the session SQL database. Empty when no session database is available. +/// The setCredentials variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadSqlTodosResult +public partial class AuthWriteSetCredentials : AuthWrite { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "setCredentials"; + + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public required string Host { get; set; } + + /// 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; } } -/// Identifies the target session. +/// Mutation request carrying the typed write command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadSqlTodosRequest +internal sealed class AccountsSetRequest { + /// 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; } -/// A single dependency edge read from the session SQL `todo_deps` table, indicating that one todo must complete before another. +/// 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 PlanSqlTodoDependency +[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 { - /// ID of the todo it depends on. - [JsonPropertyName("dependsOn")] - public string DependsOn { get; set; } = string.Empty; - - /// ID of the todo that has the dependency. - [JsonPropertyName("todoId")] - public string TodoId { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Todo rows + dependency edges read from the session SQL database. + +/// The open-url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadSqlTodosWithDependenciesResult +public partial class AuthLoginStepOpenUrl : AuthLoginStep { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "open-url"; - /// 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; } + /// Authorize URL the consumer should open in a browser (consumer-driven browser-open). + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Identifies the target session. +/// The input-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadSqlTodosWithDependenciesRequest +public partial class AuthLoginStepInputRequired : AuthLoginStep { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "input-required"; + + /// 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; } } -/// RPC data type for WorkspacesGetWorkspaceResultWorkspace operations. -public sealed class WorkspacesGetWorkspaceResultWorkspace +/// The awaiting variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepAwaiting : AuthLoginStep { - /// Current Git branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// Whether the per-session Chronicle upgrade prompt was dismissed for the workspace. - [JsonPropertyName("chronicle_sync_dismissed")] - public bool? ChronicleSyncDismissed { get; set; } - - /// Name of the client that created the workspace. - [JsonPropertyName("client_name")] - public string? ClientName { get; set; } - - /// Timestamp when the workspace was created. - [JsonPropertyName("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + /// + [JsonIgnore] + public override string Kind => "awaiting"; +} - /// Current working directory associated with the workspace. - [JsonPropertyName("cwd")] - public string? Cwd { 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"; +} - /// Git repository root associated with the workspace. - [JsonPropertyName("git_root")] - public string? GitRoot { get; set; } +/// A credential-free account choice after sign-in. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AuthLoginAccount +{ + /// Host coordinate owned by the selected account's provider. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Allowed values for the `WorkspacesWorkspaceDetailsHostType` enumeration. - [JsonPropertyName("host_type")] - public WorkspacesWorkspaceDetailsHostType? HostType { get; set; } + /// Provider kind that owns this account choice. + [JsonPropertyName("kind")] + public AccountKind Kind { get; 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; + /// Human-readable login for the account choice. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Most recent Mission Control event identifier observed for the workspace. - [JsonPropertyName("mc_last_event_id")] - public string? McLastEventId { get; set; } + /// Opaque identifier supplied to the next login step to select this account. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; +} - /// Mission Control session identifier associated with the workspace. - [JsonPropertyName("mc_session_id")] - public string? McSessionId { get; set; } +/// Result of an interactive login flow. Pending consent or account selection is not terminal. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AuthLoginResultDto +{ + /// Available accounts when sign-in is awaiting account selection, ordered with Microsoft 365 first. + [JsonPropertyName("accounts")] + public IList? Accounts { get; set; } - /// Mission Control task identifier associated with the workspace. - [JsonPropertyName("mc_task_id")] - public string? McTaskId { get; set; } + /// Host that was signed in, when completed. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string? Host { get; set; } - /// Workspace display name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Login that was signed in, when completed. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Whether the workspace session can be steered remotely. - [JsonPropertyName("remote_steerable")] - public bool? RemoteSteerable { get; set; } + /// Current disposition of the login, including pending user decisions. + [JsonPropertyName("status")] + public AuthLoginResultStatus Status { get; set; } +} - /// Repository identifier associated with the workspace. - [JsonPropertyName("repository")] - public string? Repository { get; set; } +/// The completed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepCompleted : AuthLoginStep +{ + /// + [JsonIgnore] + public override string Kind => "completed"; - /// Number of persisted summaries in the workspace. - [JsonPropertyName("summary_count")] - public long? SummaryCount { get; set; } + /// 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; } +} - /// Timestamp when the workspace was last updated. - [JsonPropertyName("updated_at")] - public DateTimeOffset? UpdatedAt { get; set; } +/// The error variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepError : AuthLoginStep +{ + /// + [JsonIgnore] + public override string Kind => "error"; - /// Whether the workspace name was explicitly chosen by the user. - [JsonPropertyName("user_named")] - public bool? UserNamed { get; set; } + /// Human-readable failure message. + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Current workspace metadata for the session, including its absolute filesystem path when available. +/// A started login flow: its opaque id and first step. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesGetWorkspaceResult +public sealed class AuthLoginBegun { - /// 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; } + /// Opaque flow id used to advance or cancel this login. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; - /// Current workspace metadata, or null if not available. - [JsonPropertyName("workspace")] - public WorkspacesGetWorkspaceResultWorkspace? Workspace { get; set; } + /// The first step of the flow. + [JsonPropertyName("step")] + public AuthLoginStep Step { get => field ??= new(); set; } } -/// Identifies the target session. +/// Begin an interactive login flow for a provider kind. Dispatch is kind-only. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesGetWorkspaceRequest +internal sealed class AuthLoginBeginRequest { + /// The provider kind to sign in with. + [JsonPropertyName("kind")] + public LoginProviderKind Kind { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Workspace metadata fields to update. +/// Advance an in-flight login flow, optionally fulfilling an input-required step. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesUpdateMetadataRequest +internal sealed class AuthLoginAdvanceRequest { - /// Opaque workspace context supplied by the session host. - [JsonPropertyName("context")] - public JsonElement? Context { get; set; } + /// Opaque flow id from begin. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; - /// Optional workspace display name override. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Neutral input fulfilling a preceding input-required step (e.g. a GHEC host); ignored otherwise. + [JsonPropertyName("input")] + public string? Input { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Optional session context used when creating a local workspace. +/// Cancel an in-flight login flow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesEnsureRequest +internal sealed class AuthLoginCancelRequest { - /// Opaque workspace context supplied by the session host. - [JsonPropertyName("context")] - public JsonElement? Context { get; set; } + /// 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; } -/// Relative paths of files stored in the session workspace files directory. +/// A file included in the session debug bundle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesListFilesResult +public sealed class DebugCollectLogsCollectedEntry { - /// Slash-separated relative file paths in the workspace files directory. - [JsonPropertyName("files")] - public IList Files { get => field ??= []; set; } -} + /// Relative path of the file in the staged bundle/archive. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; -/// 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; + /// Redacted output size in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } + + /// Source category for this entry. + [JsonPropertyName("source")] + public DebugCollectLogsSource Source { get; set; } } -/// Contents of the requested workspace file as a UTF-8 string. +/// An optional debug bundle entry that could not be included. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadFileResult +public sealed class DebugCollectLogsSkippedEntry { - /// File content as a UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// 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; } + + /// Reason the entry was skipped. + [JsonPropertyName("reason")] + public string Reason { get; set; } = string.Empty; } -/// Relative path of the workspace file to read. +/// Result of collecting a session debug bundle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesReadFileRequest +public sealed class DebugCollectLogsResult { - /// Slash-separated relative path within the workspace files directory. + /// Files included in the bundle. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } + + /// Destination kind that was written. + [JsonPropertyName("kind")] + public DebugCollectLogsResultKind Kind { 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; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional files or directories that could not be included. + [JsonPropertyName("skippedEntries")] + public IList? SkippedEntries { get; set; } } -/// Relative path and UTF-8 content for the workspace file to create or overwrite. +/// A caller-provided server-local file or directory to include in the debug bundle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesCreateFileRequest +public sealed class DebugCollectLogsEntry { - /// File content to write as a UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Relative path to use inside the staged bundle/archive. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// Slash-separated relative path within the workspace files directory. + /// Kind of source path to include. + [JsonPropertyName("kind")] + public DebugCollectLogsEntryKind Kind { get; set; } + + /// Server-local source path to read. [JsonPropertyName("path")] public string Path { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } } -/// Filesystem metadata for a path in the session workspace files directory. +/// Destination for the session debug bundle. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesStatFileResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(DebugCollectLogsDestinationArchive), "archive")] +[JsonDerivedType(typeof(DebugCollectLogsDestinationDirectory), "directory")] +public partial class DebugCollectLogsDestination { - /// Creation time in Unix epoch milliseconds. - [JsonPropertyName("birthtimeMs")] - public double BirthtimeMs { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Whether the path identifies a directory. - [JsonPropertyName("isDirectory")] - public bool IsDirectory { get; set; } - /// Whether the path identifies a regular file. - [JsonPropertyName("isFile")] - public bool IsFile { get; set; } +/// The archive variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class DebugCollectLogsDestinationArchive : DebugCollectLogsDestination +{ + /// + [JsonIgnore] + public override string Kind => "archive"; - /// Last modification time in Unix epoch milliseconds. - [JsonPropertyName("mtimeMs")] - public double MtimeMs { get; 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; } - /// Size in bytes. - [JsonPropertyName("size")] - public double Size { get; set; } + /// Absolute or server-relative path for the .tgz archive to create. + [JsonPropertyName("outputPath")] + public required string OutputPath { get; set; } } -/// Relative path of the workspace file or directory to inspect. +/// The directory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesStatFileRequest +public partial class DebugCollectLogsDestinationDirectory : DebugCollectLogsDestination { - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "directory"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Directory where files should be staged. The directory is created if needed. + [JsonPropertyName("outputDirectory")] + public required string OutputDirectory { get; set; } } -/// Directory to create within the session workspace files directory. +/// Built-in session diagnostics to include in the bundle. Omitted fields default to true. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesCreateDirectoryRequest +public sealed class DebugCollectLogsInclude { - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// 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; } - /// Whether to create missing parent directories. Defaults to false. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// Include the session event log (`events.jsonl`). Defaults to true. + [JsonPropertyName("events")] + public bool? Events { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// Include process logs for the session. Defaults to true. + [JsonPropertyName("processLogs")] + public bool? ProcessLogs { get; set; } + + /// Include interactive shell logs written under the session's `shell-logs` directory. Defaults to true. + [JsonPropertyName("shellLogs")] + public bool? ShellLogs { get; set; } } -/// File or directory to remove from the session workspace files directory. +/// Options for collecting a session debug bundle with configurable redaction. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesRemovePathRequest +internal sealed class DebugCollectLogsRequest { - /// Whether a missing path should be treated as success. Defaults to false. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// 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; } - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// 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 to remove directory contents recursively. Defaults to false. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// Which built-in session diagnostics to include. Omitted fields default to true. + [JsonPropertyName("include")] + public DebugCollectLogsInclude? Include { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Source and destination paths for a rename within the session workspace files directory. +/// 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 WorkspacesRenamePathRequest +public sealed class CanvasAction { - /// Slash-separated destination path relative to the workspace files directory. - [JsonPropertyName("destination")] - public string Destination { get; set; } = string.Empty; + /// Description of the action. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// JSON Schema for the action input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } - /// Slash-separated source path relative to the workspace files directory. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// Action name exposed by the canvas provider. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename. +/// Canvas available in the current session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesCheckpoints +public sealed class DiscoveredCanvas { - /// Filename of the checkpoint within the workspace checkpoints directory. - [JsonPropertyName("filename")] - public string Filename { get; set; } = string.Empty; + /// Actions the agent or host may invoke on an open instance. + [JsonPropertyName("actions")] + public IList? Actions { get; set; } - /// Checkpoint number assigned by the workspace manager. - [JsonPropertyName("number")] - public long Number { get; set; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Human-readable checkpoint title. - [JsonPropertyName("title")] - public string Title { 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; } + + /// JSON Schema for canvas open input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } } -/// Workspace checkpoints in chronological order; empty when the workspace is not enabled. +/// Declared canvases available in this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesListCheckpointsResult +public sealed class CanvasList { - /// Workspace checkpoints in chronological order. Empty when workspace is not enabled. - [JsonPropertyName("checkpoints")] - public IList Checkpoints { get => field ??= []; set; } + /// 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 SessionWorkspacesListCheckpointsRequest +internal sealed class SessionCanvasListRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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; } -} - -/// Checkpoint number to read. +/// Open canvas instance snapshot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesReadCheckpointRequest +public sealed class OpenCanvasInstance { - /// Checkpoint number to read. - [JsonPropertyName("number")] - public long Number { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; -/// Metadata for the persisted summary. -public sealed class WorkspacesAddSummaryResultSummary -{ -} + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; -/// Refreshed metadata for the containing workspace. -public sealed class WorkspacesAddSummaryResultWorkspace -{ -} + /// Owning extension display name, when available. + [JsonPropertyName("extensionName")] + public string? ExtensionName { get; set; } -/// Persisted summary metadata and refreshed workspace metadata. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesAddSummaryResult -{ - /// Metadata for the persisted summary. - [JsonPropertyName("summary")] - public WorkspacesAddSummaryResultSummary? Summary { get; set; } + /// Host-local PNG path for the canvas icon, when supplied. + [JsonPropertyName("icon")] + public string? Icon { get; set; } - /// Refreshed metadata for the containing workspace. - [JsonPropertyName("workspace")] - public WorkspacesAddSummaryResultWorkspace? Workspace { get; set; } -} + /// Input supplied when the instance was opened. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } -/// Compaction summary checkpoint to persist. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesAddSummaryRequest -{ - /// Markdown summary content to persist. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Stable caller-supplied canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provider-supplied status text. + [JsonPropertyName("status")] + public string? Status { get; set; } - /// Summary title shown in checkpoint listings. + /// Rendered title. [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; -} - -/// Rollback point for local workspace summaries. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesTruncateSummariesRequest -{ - /// Number of newest summaries to keep. - [JsonPropertyName("keepCount")] - public long KeepCount { get; set; } + public string? Title { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// URL for web-rendered canvases. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// Autopilot objective file content, or null when missing. +/// Live open-canvas snapshot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadAutopilotObjectiveResult +public sealed class CanvasListOpenResult { - /// Autopilot objective file content, or null when missing. - [JsonPropertyName("content")] - public string? Content { 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 SessionWorkspacesReadAutopilotObjectiveRequest +internal sealed class SessionCanvasListOpenRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of writing the autopilot objective file. +/// Canvas open parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesWriteAutopilotObjectiveResult +internal sealed class CanvasOpenRequest { - /// Filesystem operation performed. - [JsonPropertyName("operation")] - public string Operation { get; set; } = string.Empty; -} + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; -/// Autopilot objective file content to persist. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesWriteAutopilotObjectiveRequest -{ - /// Autopilot objective file content. - [JsonPropertyName("content")] - public string Content { 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; } -/// Result of deleting the autopilot objective file. +/// Canvas close parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesDeleteAutopilotObjectiveResult +internal sealed class CanvasCloseRequest { - /// True when a file was deleted. - [JsonPropertyName("deleted")] - public bool Deleted { get; set; } -} + /// Open canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesDeleteAutopilotObjectiveRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Whether the autopilot objective file exists. +/// Canvas action invocation result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesAutopilotObjectiveExistsResult +public sealed class CanvasActionInvokeResult { - /// True when the objective file exists. - [JsonPropertyName("exists")] - public bool Exists { get; set; } + /// Provider-supplied action result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// Identifies the target session. +/// Canvas action invocation parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesAutopilotObjectiveExistsRequest +internal sealed class CanvasActionInvokeRequest { + /// 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; } -/// RPC data type for WorkspacesSaveLargePasteResultSaved operations. -public sealed class WorkspacesSaveLargePasteResultSaved +/// Internal canvas provider registration parameters. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class CanvasProviderRegisterRequest { - /// Filename within the workspace files directory. - [JsonPropertyName("filename")] - public string Filename { get; set; } = string.Empty; + /// Canvas contributions supplied by the provider. + [JsonPropertyName("canvases")] + public IList Canvases { get => field ??= []; set; } - /// Absolute filesystem path to the saved paste file. - [JsonPropertyName("filePath")] - public string FilePath { get; set; } = string.Empty; + /// Connection identifier for callback routing. + [JsonPropertyName("connectionId")] + public string ConnectionId { get; set; } = string.Empty; - /// Size of the saved file in bytes. - [JsonPropertyName("sizeBytes")] - public long SizeBytes { get; set; } -} + /// Provider metadata supplied by the host. + [JsonPropertyName("info")] + public JsonElement Info { 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Pasted content to save as a UTF-8 file in the session workspace. +/// Internal canvas provider unregistration parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesSaveLargePasteRequest +internal sealed class CanvasProviderUnregisterRequest { - /// Pasted content to save as a UTF-8 file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// 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; } -/// 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. +/// Machine-readable workflow run failure. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffContents +[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 { - /// 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; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// A single changed file and its unified diff. + +/// The workflow_limit_reached variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffFileChange +public partial class WorkflowRunFailureWorkflowLimitReached : WorkflowRunFailure { - /// Type of change represented by this file diff. - [JsonPropertyName("changeType")] - public WorkspaceDiffFileChangeType ChangeType { 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; } + /// + [JsonIgnore] + public override string Type => "workflow_limit_reached"; - /// Unified diff content for the file. Empty when the diff was truncated. - [JsonPropertyName("diff")] - public string Diff { get; set; } = string.Empty; + /// Resource ceiling that stopped the run. + [JsonPropertyName("kind")] + public required WorkflowRunFailureKind Kind { get; set; } - /// Whether the diff content was omitted because it exceeded the per-file size limit. - [JsonPropertyName("isTruncated")] - public bool? IsTruncated { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } - /// Original file path for renamed files. - [JsonPropertyName("oldPath")] - public string? OldPath { get; set; } + /// Suggested larger ceiling when the runtime can derive one safely. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("suggestedValue")] + public double? SuggestedValue { 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; + /// Approved effective ceiling that was reached. + [JsonPropertyName("value")] + public required double Value { get; set; } } -/// Workspace diff result for the requested mode. +/// The workflow_resume_declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffResult +public partial class WorkflowRunFailureWorkflowResumeDeclined : WorkflowRunFailure { - /// Default branch used for a branch diff, when branch mode was requested. - [JsonPropertyName("baseBranch")] - public string? BaseBranch { get; set; } + /// + [JsonIgnore] + public override string Type => "workflow_resume_declined"; - /// Changed files and their unified diffs. - [JsonPropertyName("changes")] - public IList Changes { get => field ??= []; set; } + /// Human-readable reason the resume did not proceed. + [JsonPropertyName("reason")] + public required string Reason { get; 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; } + /// Workflow run identifier whose changed limits were declined. + [JsonPropertyName("runId")] + public required string RunId { get; set; } +} - /// Effective mode used for the returned changes. - [JsonPropertyName("mode")] - public WorkspaceDiffMode Mode { get; set; } +/// The workflow_durable_failure variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class WorkflowRunFailureWorkflowDurableFailure : WorkflowRunFailure +{ + /// + [JsonIgnore] + public override string Type => "workflow_durable_failure"; - /// Diff mode requested by the client. - [JsonPropertyName("requestedMode")] - public WorkspaceDiffMode RequestedMode { get; set; } + /// Stable failure code. + [JsonPropertyName("code")] + public required string Code { 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; } + /// Execution-critical durable operation that failed. + [JsonPropertyName("operation")] + public required WorkflowDurableOperation Operation { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Parameters for computing a workspace diff. +/// 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 WorkspacesDiffRequest +public partial class WorkflowRunFailureWorkflowAccountingIncomplete : WorkflowRunFailure { - /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. - [JsonPropertyName("ignoreWhitespace")] - public bool? IgnoreWhitespace { get; set; } + /// + [JsonIgnore] + public override string Type => "workflow_accounting_incomplete"; - /// 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; } + /// Confirmed usage in nano-AIU, representing the floor of what the run spent. + [JsonPropertyName("drainedNanoAiu")] + public required long DrainedNanoAiu { get; set; } - /// Diff mode requested by the client. - [JsonPropertyName("mode")] - public required WorkspaceDiffMode Mode { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Parameters for computing a workspace diff. +/// 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)] -internal sealed class WorkspacesDiffRequestWithSession +public partial class WorkflowRunFailureWorkflowProviderDisconnected : WorkflowRunFailure { - /// 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; } + /// + [JsonIgnore] + public override string Type => "workflow_provider_disconnected"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Current per-window credit limit and consumption for an autopilot objective. +/// Durable metadata describing who initiated a workflow pause. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveCreditLimit +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(WorkflowPauseInfoUser), "user")] +[JsonDerivedType(typeof(WorkflowPauseInfoCheckpoint), "checkpoint")] +public partial class WorkflowPauseInfo { - /// Configured AI-credit cap, when one is set. - [JsonPropertyName("credits")] - public double? Credits { get; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { 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; +/// The user variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class WorkflowPauseInfoUser : WorkflowPauseInfo +{ + /// + [JsonIgnore] + public override string Type => "user"; } -/// Public, persistence-independent projection of an autopilot objective. +/// The checkpoint variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveState +public partial class WorkflowPauseInfoCheckpoint : WorkflowPauseInfo { - /// Optional summary recorded when the objective completed. - [JsonPropertyName("completionSummary")] - public string? CompletionSummary { get; set; } + /// + [JsonIgnore] + public override string Type => "checkpoint"; - /// 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; + /// Stable author-defined checkpoint key that initiated the pause. + [JsonPropertyName("key")] + public required string Key { get; set; } +} - /// Current per-window consumption and optional cap, when a credit-tracking window is present. - [JsonPropertyName("creditLimit")] - public AutopilotObjectiveCreditLimit? CreditLimit { get; set; } +/// Complete current or terminal workflow run envelope. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkflowRunResult +{ + /// 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; } - /// Session-local objective identifier. - [JsonPropertyName("id")] - public long Id { get; set; } + /// Error message for an errored run. + [JsonPropertyName("error")] + public string? Error { 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; } + /// Machine-readable failure details for a halted or errored run. + [JsonPropertyName("failure")] + public WorkflowRunFailure? Failure { get; set; } - /// Current normalized lifecycle status. - [JsonPropertyName("status")] - public AutopilotObjectiveStatus Status { get; set; } + /// Structured pause initiator metadata for a paused attempt. + [JsonPropertyName("pauseInfo")] + public WorkflowPauseInfo? PauseInfo { get; set; } - /// Number of objective turns started. - [JsonPropertyName("turnCount")] - public long TurnCount { get; set; } -} + /// Reason for a halted or cancelled run. + [JsonPropertyName("reason")] + public string? Reason { 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; } -} + /// Completed workflow result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAutopilotObjectiveGetStateRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { 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`). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CompletionsGetTriggerCharactersResult -{ - /// Trigger characters advertised by the host (e.g. `["@", "#"]`). Empty disables host-driven completions for the session. - [JsonPropertyName("triggerCharacters")] - public IList TriggerCharacters { get => field ??= []; set; } -} + /// Partial journal and progress snapshot for a halted, cancelled, or errored run. + [JsonPropertyName("snapshot")] + public JsonElement? Snapshot { 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; + /// Current or terminal workflow run status. + [JsonPropertyName("status")] + public WorkflowRunStatus Status { get; set; } } -/// 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. +/// Wire-only per-invocation workflow resource ceiling overrides. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionCompletionItem +public sealed class WorkflowRunLimits { - /// Text spliced into the composer when the item is accepted. - [JsonPropertyName("insertText")] - public string InsertText { get; set; } = string.Empty; - - /// 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; } + /// 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; } - /// Primary display label for the picker row. Falls back to `insertText` when absent. - [JsonPropertyName("label")] - public string? Label { get; set; } + /// Maximum number of workflow subagents that may run concurrently. + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } - /// End (exclusive) of the replacement range in `text`, in UTF-16 code units. - [JsonPropertyName("rangeEnd")] - public long? RangeEnd { get; set; } + /// Maximum total number of workflow subagents that may be admitted. + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } - /// Start of the replacement range in `text`, in UTF-16 code units. - [JsonPropertyName("rangeStart")] - public long? RangeStart { 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; } } -/// Host-driven completion items for the current composer input. Empty when the host returns no items or does not support completions. +/// Options controlling workflow invocation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CompletionsRequestResult +public sealed class WorkflowRunOptions { - /// Completion items in host-ranked order. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } -} + /// Per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { 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; } + /// Whether to emit workflow phase names to the session transcript. + [JsonPropertyName("logPhaseNames")] + public bool? LogPhaseNames { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether to notify the originating session when the workflow completes. + [JsonPropertyName("notifyOnComplete")] + public bool? NotifyOnComplete { get; set; } - /// The full composed composer input. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// Run identifier whose journal and progress should seed this resumed run. + [JsonPropertyName("resumeFromRunId")] + public string? ResumeFromRunId { get; set; } } -/// Instruction sources loaded for the session, in merge order. +/// Parameters for invoking a registered workflow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstructionsGetSourcesResult +internal sealed class WorkflowRunRequest { - /// Instruction sources for the session. - [JsonPropertyName("sources")] - public IList Sources { get => field ??= []; set; } -} + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionInstructionsGetSourcesRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Workflow invocation options. + [JsonPropertyName("options")] + public WorkflowRunOptions? Options { 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; } -/// Result of one customization reload component. +/// Resolved persisted workflow identity and resumed run envelope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CustomizationReloadOutcome +public sealed class WorkflowResumeResult { - /// Reason for a skipped component or description of a failure, when available. - [JsonPropertyName("detail")] - public string? Detail { get; set; } - - /// Whether the component reloaded, was skipped, or failed. - [JsonPropertyName("status")] - public CustomizationReloadStatus Status { get; set; } + /// Terminal resumed run envelope. + [JsonPropertyName("run")] + public WorkflowRunResult Run { get => field ??= new(); set; } - /// Component whose reload was attempted or skipped. - [JsonPropertyName("subsystem")] - public CustomizationReloadSubsystem Subsystem { get; set; } + /// Persisted workflow name resolved for the resumed run. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// 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. +/// Parameters for resuming a workflow run from its persisted identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CustomizationsReloadResult +internal sealed class WorkflowResumeRequest { - /// Errors from any component that could not be refreshed. - [JsonPropertyName("errors")] - public IList Errors { get => field ??= []; set; } + /// Optional per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { 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; } + /// Whether to emit workflow phase names to the session transcript. + [JsonPropertyName("logPhaseNames")] + public bool? LogPhaseNames { get; set; } - /// Warnings from skill discovery. - [JsonPropertyName("warnings")] - public IList Warnings { get => field ??= []; 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; -/// 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; } -/// Indicates whether fleet mode was successfully activated. +/// Options for an internal tool-originated workflow invocation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class FleetStartResult +internal sealed class WorkflowToolRunOptions { - /// Whether fleet mode was successfully activated. - [JsonPropertyName("started")] - public bool Started { get; set; } + /// Per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } + + /// Run identifier whose journal and progress should seed this resumed run. + [JsonPropertyName("resumeFromRunId")] + public string? ResumeFromRunId { 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. +/// Internal parameters for invoking a registered workflow from a tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FleetStartRequest +internal sealed class WorkflowToolRunRequest { - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the fleet request. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { 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; } + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Optional user prompt to combine with fleet instructions. - [JsonPropertyName("prompt")] - public string? Prompt { get; set; } + /// Tool-originated workflow invocation options. + [JsonPropertyName("options")] + public WorkflowToolRunOptions? Options { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// If true, await completion of the agentic loop for this fleet request before returning. Defaults to false. - [JsonPropertyName("wait")] - public bool? Wait { 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; } -} - -/// 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; } - - /// 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; } + /// Opaque identifier of the originating tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } } -/// RPC data type for SessionAgentListRequestWithSession operations. +/// Internal parameters for resuming a workflow run from a tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentListRequestWithSession +internal sealed class WorkflowToolResumeRequest { - /// 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 per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { 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; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} - -/// An in-memory authored prompt override for an available agent. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -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; + /// Opaque identifier of the originating tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } } -/// The currently selected custom agent, or null when using the default agent. +/// Parameters for retrieving a workflow run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentGetCurrentResult +internal sealed class WorkflowGetRunRequest { - /// Currently selected custom agent, or null if using the default agent. - [JsonPropertyName("agent")] - public AgentInfo? Agent { 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 SessionAgentGetCurrentRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The newly selected custom agent. +/// Declared or approved workflow resource ceilings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentSelectResult +public sealed class WorkflowDeclaredLimits { - /// The newly selected custom agent. - [JsonPropertyName("agent")] - public AgentInfo Agent { get => field ??= new(); set; } -} + /// Maximum AI credits consumed by subagents and descendants. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } -/// Name of the custom agent to select for subsequent turns. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentSelectRequest -{ - /// Name of the custom agent to select. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Maximum concurrently active subagents. + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Maximum total subagents spawned by the run. + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentDeselectRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Maximum accumulated active execution time in seconds. + [JsonPropertyName("timeoutSeconds")] + public double? TimeoutSeconds { get; set; } } -/// Custom agents available to the session after reloading definitions from disk. +/// Durable workflow resource consumption. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentReloadResult +public sealed class WorkflowRunConsumed { - /// Reloaded custom agents. - [JsonPropertyName("agents")] - public IList Agents { 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 SessionAgentReloadRequest -{ - /// 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; } } -/// Identifier assigned to the newly started background agent task. +/// Current workflow phase identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksStartAgentResult +public sealed class WorkflowCurrentPhase { - /// Generated agent ID for the background task. - [JsonPropertyName("agentId")] - public string AgentId { get; set; } = string.Empty; + /// Current phase identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Zero-based declared phase ordinal, or null for an undeclared phase. + [JsonPropertyName("ordinal")] + public long? Ordinal { get; set; } } -/// Agent type, prompt, name, and optional description and model override for the new task. +/// Prompt-safe terminal workflow outcome. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksStartAgentRequest +public sealed class WorkflowRunTerminal { - /// 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; } + /// Human-readable terminal error. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Optional model override. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Machine-readable terminal failure. + [JsonPropertyName("failure")] + public WorkflowRunFailure? Failure { get; set; } - /// Friendly, non-unique name used when displaying the agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Pause initiator metadata, or null when the run did not pause. + [JsonPropertyName("pauseInfo")] + public WorkflowPauseInfo? PauseInfo { get; set; } - /// Task prompt for the agent. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Human-readable terminal reason. + [JsonPropertyName("reason")] + public string? Reason { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Prompt-safe preview of the completed result. + [JsonPropertyName("resultPreview")] + public string? ResultPreview { get; set; } } -/// Tracked task union returned by task APIs, containing an agent, client, or shell task. -/// Polymorphic base type discriminated by type. +/// Durable workflow run summary with read-time live overlays. [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 WorkflowRunSummary { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Epoch milliseconds when the current active segment started, or null while inactive. + [JsonPropertyName("activeSegmentStartedAt")] + public long? ActiveSegmentStartedAt { get; set; } + /// Approved effective resource ceilings, or null until approved. + [JsonPropertyName("approved")] + public WorkflowDeclaredLimits? Approved { 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 durable run state currently passes runtime resume eligibility checks. + [JsonPropertyName("canResume")] + public bool CanResume { get; set; } - /// ISO 8601 timestamp when the current active period began. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } + /// Epoch milliseconds when the run completed, or null while nonterminal. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } - /// Accumulated active execution time in milliseconds. - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeTimeMs")] - public TimeSpan? ActiveTime { get; set; } + /// Durable resource consumption. + [JsonPropertyName("consumed")] + public WorkflowRunConsumed Consumed { get => field ??= new(); set; } - /// Type of agent running this task. - [JsonPropertyName("agentType")] - public required string AgentType { get; set; } + /// Epoch milliseconds when the run was created. + [JsonPropertyName("createdAt")] + public long CreatedAt { 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; } + /// Current phase identity, or null before any phase is entered. + [JsonPropertyName("currentPhase")] + public WorkflowCurrentPhase? CurrentPhase { get; set; } - /// ISO 8601 timestamp when the task finished. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Resource ceilings declared by the workflow. + [JsonPropertyName("declaredLimits")] + public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } - /// Short description of the task. - [JsonPropertyName("description")] - public required string Description { get; set; } - - /// Friendly, non-unique name intended for display. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } - - /// Error message when the task failed. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Number of phases declared by the workflow. + [JsonPropertyName("declaredPhaseCount")] + public long DeclaredPhaseCount { get; set; } - /// Whether task execution is synchronously awaited or managed in the background. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("executionMode")] - public TaskExecutionMode? ExecutionMode { get; set; } + /// Human-readable workflow description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Unique task identifier. - [JsonPropertyName("id")] - public required string Id { get; set; } + /// Number of direct workflow agents currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } - /// ISO 8601 timestamp when the agent entered idle state. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } + /// Epoch milliseconds when this live-overlay snapshot was observed. + [JsonPropertyName("observedAt")] + public long ObservedAt { get; set; } - /// Most recent response text from the agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("latestResponse")] - public string? LatestResponse { get; set; } + /// Monotonic durable run revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } - /// Requested model override for the task when specified. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { 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; } + /// Epoch milliseconds when execution first started, or null before start. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } - /// Runtime model resolved for the task when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("resolvedModel")] - public string? ResolvedModel { get; set; } + /// Current workflow run status. + [JsonPropertyName("status")] + public WorkflowRunStatus Status { get; set; } - /// Result text from the task when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public string? Result { get; set; } + /// Terminal run outcome, or null while nonterminal. + [JsonPropertyName("terminal")] + public WorkflowRunTerminal? Terminal { get; set; } - /// ISO 8601 timestamp when the task was started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + /// Total direct workflow agents spawned across all attempts. + [JsonPropertyName("totalSpawnedAgentCount")] + public long TotalSpawnedAgentCount { get; set; } - /// Current lifecycle status of the task. - [JsonPropertyName("status")] - public required TaskStatus Status { get; set; } + /// Epoch milliseconds when the durable run was last updated. + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } - /// Tool call ID associated with this agent task. - [JsonPropertyName("toolCallId")] - public required string ToolCallId { get; set; } + /// Registered workflow name. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Public owner attribution for a client-owned task. Identifiers are opaque and never authorize requests. +/// A page of workflow runs in durable creation order. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskClientOwner +public sealed class WorkflowListRunsResult { - /// ISO 8601 timestamp when the bound join disconnected. - [JsonPropertyName("disconnectedAt")] - public DateTimeOffset? DisconnectedAt { get; set; } - - /// Display-only owner name. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } - - /// Opaque identity of the currently or most recently bound session join. - [JsonPropertyName("joinId")] - public string JoinId { get; set; } = string.Empty; + /// Whether terminal runs newer than this page exist. + [JsonPropertyName("hasMoreNewer")] + public bool? HasMoreNewer { get; set; } - /// Class of the task owner. - [JsonPropertyName("kind")] - public TaskClientOwnerKind Kind { get; set; } + /// Newest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("newestSeq")] + public long? NewestSeq { get; set; } - /// Opaque session-scoped participant identity. - [JsonPropertyName("participantId")] - public string ParticipantId { get; set; } = string.Empty; + /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("oldestSeq")] + public long? OldestSeq { get; set; } - /// Whether this task's bound join is currently connected. - [JsonPropertyName("presence")] - public TaskClientOwnerPresence Presence { get; set; } + /// Number of terminal runs older than this page. + [JsonPropertyName("omittedOlder")] + public long? OmittedOlder { get; set; } - /// Display-only owner source. - [JsonPropertyName("source")] - public string? Source { get; set; } + /// Workflow run summaries in durable creation order. + [JsonPropertyName("runs")] + public IList Runs { get => field ??= []; set; } } -/// Tracked client-owned task metadata. -/// The client variant of . +/// Parameters for paging workflow runs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoClient : TaskInfo +internal sealed class WorkflowListRunsRequest { - /// - [JsonIgnore] - public override string Type => "client"; + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } - /// ISO 8601 timestamp when the current active segment started. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeTimeMs")] - public required long ActiveTimeMs { get; set; } + /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } - /// Whether the currently bound owner can receive a cancellation request. - [JsonPropertyName("canCancel")] - public required bool CanCancel { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Human-readable reason for terminal cancellation. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("cancellationReason")] - public string? CancellationReason { get; set; } +/// Prompt-safe durable identity and live status for a direct workflow agent. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkflowAgentSummary +{ + /// Accumulated active agent time in milliseconds. + [JsonPropertyName("activeMs")] + public long ActiveMs { get; set; } - /// Owner-scoped registration and reclaim key. - [JsonPropertyName("clientTaskId")] - public required string ClientTaskId { get; set; } + /// Prompt-safe live activity text. + [JsonPropertyName("activity")] + public string? Activity { get; set; } - /// ISO 8601 timestamp when the task reached a terminal status. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Stable direct-agent identifier. + [JsonPropertyName("agentId")] + public string AgentId { get; set; } = string.Empty; - /// Task description. - [JsonPropertyName("description")] - public required string Description { get; set; } + /// Registered agent type. + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; - /// Optional task display name. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Epoch milliseconds when the agent completed. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } + + /// Friendly, non-unique name intended for display. [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; } + /// Friendly, non-unique name intended for display. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; - /// Public attribution and presence for the task owner. - [JsonPropertyName("owner")] - public required TaskClientOwner Owner { get; set; } + /// Phase identifier active when the agent was launched, or null. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } - /// ISO 8601 timestamp of the most recent successful reclaim. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reclaimedAt")] - public DateTimeOffset? ReclaimedAt { get; set; } + /// Model requested when the agent was launched. + [JsonPropertyName("requestedModel")] + public string? RequestedModel { get; set; } - /// Opaque successful terminal result supplied by the task owner. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Concrete model resolved for the agent. + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { get; set; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public required long Sequence { get; set; } + /// Owning workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// ISO 8601 timestamp when the task started. + /// Epoch milliseconds when the agent started. [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + public long? StartedAt { get; set; } - /// Client task lifecycle status. + /// Current durable or live agent status. [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; } + /// Tool-call identifier that launched the agent. + [JsonPropertyName("toolCallId")] + public string ToolCallId { get; set; } = string.Empty; } -/// Tracked shell task metadata, including ID, command, status, timing, attachment/execution mode, log path, and PID. -/// The shell variant of . +/// Durable lifecycle and timing for one workflow phase. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoShell : TaskInfo +public sealed class WorkflowPhaseObservation { - /// - [JsonIgnore] - public override string Type => "shell"; - - /// 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; } + /// Completed active time accumulated by this phase in milliseconds. + [JsonPropertyName("accumulatedActiveMs")] + public long AccumulatedActiveMs { get; set; } - /// ISO 8601 timestamp when the task finished. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Epoch milliseconds when this phase completed; for a skipped phase, the synthetic skip timestamp (equal to `startedAt`). [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + public long? CompletedAt { get; set; } - /// Short description of the task. - [JsonPropertyName("description")] - public required string Description { get; set; } + /// Current live active time for this phase in milliseconds. + [JsonPropertyName("currentActiveMs")] + public long CurrentActiveMs { get; set; } - /// Whether task execution is synchronously awaited or managed in the background. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("executionMode")] - public TaskExecutionMode? ExecutionMode { get; set; } + /// Optional human-readable phase detail. + [JsonPropertyName("detail")] + public string? Detail { get; set; } - /// Unique task identifier. + /// Number of times execution entered this phase. + [JsonPropertyName("entryCount")] + public long EntryCount { get; set; } + + /// Phase identifier. [JsonPropertyName("id")] - public required string Id { get; set; } + public string Id { get; set; } = string.Empty; - /// Path to the detached shell log, when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("logPath")] - public string? LogPath { get; set; } + /// Most recent run attempt that entered this phase, or `0` if the phase has never been entered. + [JsonPropertyName("lastEnteredRunAttempt")] + public long LastEnteredRunAttempt { get; set; } - /// Process ID when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pid")] - public long? Pid { get; set; } + /// Direct agents in this phase that are currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } - /// ISO 8601 timestamp when the task was started. + /// Zero-based declared phase ordinal, or null for an undeclared phase. + [JsonPropertyName("ordinal")] + public long? Ordinal { get; set; } + + /// Epoch milliseconds when this phase first started; for a skipped phase, the synthetic skip timestamp (equal to `completedAt`). [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + public long? StartedAt { get; set; } - /// Current lifecycle status of the task. + /// Derived lifecycle state of the phase. [JsonPropertyName("status")] - public required TaskStatus Status { get; set; } + public WorkflowPhaseStatus Status { get; set; } + + /// Human-readable phase title. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; + + /// Total direct agents associated with this phase. + [JsonPropertyName("totalAgentCount")] + public long TotalAgentCount { get; set; } } -/// Background tasks currently tracked by the session. +/// One durable workflow progress record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskList +public sealed class WorkflowProgressLine { - /// Currently tracked tasks. - [JsonPropertyName("tasks")] - public IList Tasks { get => field ??= []; set; } + /// Resume attempt that emitted this record. + [JsonPropertyName("attempt")] + public long Attempt { 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; } -/// Identifies the target session. +/// A bidirectional page of workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksListRequest +public sealed class WorkflowProgressPage { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// Run revision reflected by this page. + [JsonPropertyName("revision")] + public long Revision { get; set; } } -/// Tracked client-owned task metadata. +/// Full workflow run observability detail. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskClientInfo +public sealed class WorkflowRunDetail { - /// 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; } + /// Epoch milliseconds when the current active segment started, or null while inactive. + [JsonPropertyName("activeSegmentStartedAt")] + public long? ActiveSegmentStartedAt { get; set; } - /// Whether the currently bound owner can receive a cancellation request. - [JsonPropertyName("canCancel")] - public bool CanCancel { get; set; } + /// Durable identities and live statuses for direct workflow agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } - /// Human-readable reason for terminal cancellation. - [JsonPropertyName("cancellationReason")] - public string? CancellationReason { get; set; } + /// Approved effective resource ceilings, or null until approved. + [JsonPropertyName("approved")] + public WorkflowDeclaredLimits? Approved { get; set; } - /// Owner-scoped registration and reclaim key. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; + /// Whether the durable run state currently passes runtime resume eligibility checks. + [JsonPropertyName("canResume")] + public bool CanResume { get; set; } - /// ISO 8601 timestamp when the task reached a terminal status. + /// Epoch milliseconds when the run completed, or null while nonterminal. [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + public long? CompletedAt { get; set; } - /// Task description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Durable resource consumption. + [JsonPropertyName("consumed")] + public WorkflowRunConsumed Consumed { get => field ??= new(); set; } - /// Optional task display name. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Epoch milliseconds when the run was created. + [JsonPropertyName("createdAt")] + public long CreatedAt { get; set; } - /// Human-readable terminal failure message. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Current phase identity, or null before any phase is entered. + [JsonPropertyName("currentPhase")] + public WorkflowCurrentPhase? CurrentPhase { get; set; } - /// Optional owner-supplied terminal failure code. - [JsonPropertyName("errorCode")] - public string? ErrorCode { get; set; } + /// Resource ceilings declared by the workflow. + [JsonPropertyName("declaredLimits")] + public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } - /// Execution mode, which is always background for client-owned tasks. - [JsonPropertyName("executionMode")] - public TaskClientExecutionMode ExecutionMode { get; set; } + /// Number of phases declared by the workflow. + [JsonPropertyName("declaredPhaseCount")] + public long DeclaredPhaseCount { get; set; } - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Human-readable workflow description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// ISO 8601 timestamp when the connected owner entered idle status. - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } + /// Number of direct workflow agents currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } - /// ISO 8601 timestamp of the most recent orphan transition. - [JsonPropertyName("orphanedAt")] - public DateTimeOffset? OrphanedAt { get; set; } + /// Epoch milliseconds when this live-overlay snapshot was observed. + [JsonPropertyName("observedAt")] + public long ObservedAt { get; set; } - /// Public attribution and presence for the task owner. - [JsonPropertyName("owner")] - public TaskClientOwner Owner { get => field ??= new(); set; } + /// Lifecycle and timing observations for each workflow phase. + [JsonPropertyName("phases")] + public IList Phases { get => field ??= []; set; } - /// ISO 8601 timestamp of the most recent successful reclaim. - [JsonPropertyName("reclaimedAt")] - public DateTimeOffset? ReclaimedAt { get; set; } + /// Bidirectional page of durable workflow progress. + [JsonPropertyName("progress")] + public WorkflowProgressPage Progress { get => field ??= new(); set; } - /// Opaque successful terminal result supplied by the task owner. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Monotonic durable run revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public long Sequence { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// ISO 8601 timestamp when the task started. + /// Epoch milliseconds when execution first started, or null before start. [JsonPropertyName("startedAt")] - public DateTimeOffset StartedAt { get; set; } + public long? StartedAt { get; set; } - /// Client task lifecycle status. + /// Current workflow run status. [JsonPropertyName("status")] - public TaskClientStatus Status { get; set; } - - /// Task kind. - [JsonPropertyName("type")] - public TaskClientType Type { get; set; } + public WorkflowRunStatus Status { get; set; } - /// ISO 8601 timestamp of the latest accepted lifecycle change. - [JsonPropertyName("updatedAt")] - public DateTimeOffset UpdatedAt { get; set; } -} + /// Terminal run outcome, or null while nonterminal. + [JsonPropertyName("terminal")] + public WorkflowRunTerminal? Terminal { get; set; } -/// Result of registering or reclaiming a client-owned task. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksRegisterResult -{ - /// True only when this invocation created a new task. - [JsonPropertyName("created")] - public bool Created { get; set; } + /// Total direct workflow agents spawned across all attempts. + [JsonPropertyName("totalSpawnedAgentCount")] + public long TotalSpawnedAgentCount { get; set; } - /// True only when this invocation reclaimed an orphaned task. - [JsonPropertyName("reclaimed")] - public bool Reclaimed { get; set; } + /// Epoch milliseconds when the durable run was last updated. + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } - /// Authoritative registered or reclaimed task. - [JsonPropertyName("task")] - public TaskClientInfo Task { get => field ??= new(); set; } + /// Registered workflow name. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Registers or reclaims a client-owned task. +/// Parameters for paging workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksRegisterRequest +internal sealed class WorkflowGetRunProgressRequest { - /// Whether the owner supports runtime cancellation requests. - [JsonPropertyName("cancellable")] - public bool Cancellable { get; set; } + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } - /// Owner-scoped idempotency key used for registration and reclaim. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } - /// Human-readable description of the external work. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Maximum records to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } - /// Optional short display name for the external work. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Optional phase identifier used to scope records and cursors. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } - /// Expected current sequence for idempotent registration or orphan reclaim. - [JsonPropertyName("expectedSequence")] - public long? ExpectedSequence { 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; - - /// Task kind. - [JsonPropertyName("type")] - public TaskClientType Type { get; set; } } -/// Result of publishing a client-owned task update. +/// Parameters for cancelling a workflow run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksUpdateResult +internal sealed class WorkflowCancelRequest { - /// 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; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// Authoritative task after processing the update. - [JsonPropertyName("task")] - public TaskClientInfo Task { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Progress or terminal update for a client-owned task. -/// Polymorphic base type discriminated by kind. +/// Parameters for pausing a running workflow. [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 +internal sealed class WorkflowPauseRequest { - /// 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; +} -/// Publishes nonterminal progress for a running or idle client task. -/// The progress variant of . +/// RPC data type for SessionWorkflowPauseAtCheckpoint operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateProgress : TaskClientUpdate +internal sealed class SessionWorkflowPauseAtCheckpointResult { - /// - [JsonIgnore] - public override string Kind => "progress"; - - /// Optional progress message appended to recent activity when nonempty. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Optional completion percentage; null clears the current percentage. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("percentage")] - public double? Percentage { get; set; } - - /// Optional progress phase; null clears the current phase. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("phase")] - public string? Phase { get; set; } - - /// Optional active status transition. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("status")] - public TaskClientActiveStatus? Status { get; set; } + /// Whether this execution attempt must pause or may continue. + [JsonPropertyName("action")] + public WorkflowPauseCheckpointAction Action { get; set; } } -/// Reports successful terminal completion. -/// The completed variant of . +/// Parameters for an owned durable pause checkpoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateCompleted : TaskClientUpdate +internal sealed class WorkflowPauseCheckpointRequest { - /// - [JsonIgnore] - public override string Kind => "completed"; + /// Opaque token identifying the execution attempt that reached the checkpoint. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Stable author-defined checkpoint key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Optional opaque successful terminal result. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public JsonElement? Result { 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; } -/// Reports terminal failure. -/// The failed variant of . +/// Acknowledgement that a workflow request was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateFailed : TaskClientUpdate +public sealed class WorkflowAckResult { - /// - [JsonIgnore] - public override string Kind => "failed"; - - /// Optional owner-supplied terminal failure code. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("code")] - public string? Code { get; set; } - - /// 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; } } -/// Reports terminal cancellation after external work stopped. -/// The cancelled variant of . +/// One ordered workflow progress line. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateCancelled : TaskClientUpdate +public sealed class WorkflowLogLine { - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// Progress line kind. + [JsonPropertyName("kind")] + public WorkflowLogLineKind Kind { get; set; } - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Monotonic sequence number within the workflow run. + [JsonPropertyName("seq")] + public long Seq { get; set; } - /// Optional human-readable cancellation reason. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Progress text. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; } -/// Updates a client-owned task. +/// Parameters for recording workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksUpdateRequest +internal sealed class WorkflowLogRequest { - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Owner update sequence to apply. - [JsonPropertyName("sequence")] - public long Sequence { get; set; } + /// 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; - - /// 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. +/// Result of one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksRefreshResult +public sealed class WorkflowAgentResult { + /// Agent result, omitted when the agent produced no result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// Identifies the target session. +/// Options for one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksRefreshRequest +public sealed class WorkflowAgentOptions { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Optional built-in or custom agent name whose definition configures the subagent. + [JsonPropertyName("agent")] + public string? Agent { get; set; } -/// 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)] -public sealed class TasksWaitForPendingResult -{ + /// 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; } + + /// Optional JSON Schema for structured agent output. + [JsonPropertyName("schema")] + public JsonElement? Schema { get; set; } } -/// Identifies the target session. +/// Parameters for one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksWaitForPendingRequest +internal sealed class WorkflowAgentRequest { + /// 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; } + + /// Prompt to send to the subagent. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + /// 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. -[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 -{ - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Workflow run identifier that owns the subagent. + [JsonPropertyName("workflowRunId")] + public string WorkflowRunId { get; set; } = string.Empty; } - -/// Timestamped display line for task progress output or recent agent activity. +/// Result of reading a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskProgressLine +public sealed class WorkflowJournalGetResult { - /// Display message, e.g., "▸ bash", "✓ edit src/foo.ts". - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Whether the journal contained the requested key. + [JsonPropertyName("hit")] + public bool Hit { get; set; } - /// ISO 8601 timestamp when this event occurred. - [JsonPropertyName("timestamp")] - public DateTimeOffset Timestamp { get; set; } + /// Cached JSON result. The hit field distinguishes a cached JSON null from a miss. + [JsonPropertyName("resultJson")] + public JsonElement? ResultJson { get; set; } } -/// Progress snapshot for an agent task, with recent activity lines and optional latest intent. -/// The agent variant of . +/// Parameters for reading a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressAgent : TaskProgress +internal sealed class WorkflowJournalGetRequest { - /// - [JsonIgnore] - public override string Type => "agent"; + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// The most recent intent reported by the agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("latestIntent")] - public string? LatestIntent { get; set; } + /// Namespaced journal key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Recent tool execution events converted to display lines. - [JsonPropertyName("recentActivity")] - public required IList RecentActivity { 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; } -/// Generic progress for a client-owned task. -/// The client variant of . +/// Parameters for storing a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressClient : TaskProgress +internal sealed class WorkflowJournalPutRequest { - /// - [JsonIgnore] - public override string Type => "client"; - - /// Most recent nonempty progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("lastMessage")] - public string? LastMessage { get; set; } - - /// Current completion percentage from zero through one hundred. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("percentage")] - public double? Percentage { get; set; } - - /// Current owner-defined progress phase. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("phase")] - public string? Phase { get; set; } + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Recent server-timestamped progress messages. - [JsonPropertyName("recentActivity")] - public required IList RecentActivity { get; set; } + /// Namespaced journal key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public required long Sequence { get; set; } + /// JSON result to memoize. + [JsonPropertyName("resultJson")] + public JsonElement ResultJson { get; set; } - /// Current client task lifecycle status. - [JsonPropertyName("status")] - public required TaskClientStatus Status { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { 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 . +/// 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 partial class TaskProgressShell : TaskProgress +public sealed class CurrentModel { - /// - [JsonIgnore] - public override string Type => "shell"; + /// 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; } - /// Process ID when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pid")] - public long? Pid { get; set; } + /// 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; } - /// Recent stdout/stderr lines from the running shell command. - [JsonPropertyName("recentOutput")] - public required string RecentOutput { get; set; } -} + /// Context tier for models that support multiple context-window sizes. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } -/// Progress information for the task, or null when no task with that ID is tracked. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksGetProgressResult -{ - /// 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; } -} + /// Currently active model identifier. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } -/// Identifier of the background task to fetch progress for. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksGetProgressRequest -{ - /// Task identifier (agent ID or shell ID). - [JsonPropertyName("id")] - public string Id { 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// 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; } -/// The first sync-waiting task that can currently be promoted to background mode. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksGetCurrentPromotableResult -{ - /// 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; } + /// Provider of planBaseModelId, when that saved selection is provider-qualified. + [JsonPropertyName("planBaseProviderId")] + public string? PlanBaseProviderId { 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; } + + /// 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 SessionTasksGetCurrentPromotableRequest +internal sealed class SessionModelGetCurrentRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the task was successfully promoted to background mode. +/// RPC data type for ModelSwitchConfirmation operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksPromoteToBackgroundResult +public sealed class ModelSwitchConfirmation { - /// Whether the task was successfully promoted to background mode. - [JsonPropertyName("promoted")] - public bool Promoted { get; set; } -} + /// Current conversation token count before switching models. + [JsonPropertyName("currentTokens")] + public double CurrentTokens { get; set; } -/// Identifier of the task to promote to background mode. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksPromoteToBackgroundRequest -{ - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Target model token limit used by the compaction preflight. + [JsonPropertyName("targetLimit")] + public double TargetLimit { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Display name of the model that requires compaction confirmation. + [JsonPropertyName("targetModelDisplayName")] + public string TargetModelDisplayName { get; set; } = string.Empty; } -/// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. +/// The model identifier active on the session after the switch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksPromoteCurrentToBackgroundResult +public sealed class ModelSwitchToResult { - /// 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; } -} + /// Compaction confirmation projection when status is confirmation_required. + [JsonPropertyName("confirmation")] + public ModelSwitchConfirmation? Confirmation { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksPromoteCurrentToBackgroundRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } -/// Indicates whether the background task was successfully cancelled. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksCancelResult -{ - /// Whether the task was successfully cancelled. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } -} + /// Deprecation warnings associated with the selected model or options. + [JsonPropertyName("deprecationWarnings")] + public IList? DeprecationWarnings { get; set; } -/// Identifier of the background task to cancel. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksCancelRequest -{ - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// User-facing outcome message for the model switch. + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Currently active model identifier after the switch. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } -/// 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 sealed class TasksRemoveResult -{ - /// 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; } + /// 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; } + + /// Persistence failure encountered after applying the model switch. + [JsonPropertyName("persistenceError")] + public string? PersistenceError { 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; } + + /// Lifecycle result for the requested switch. + [JsonPropertyName("status")] + public string? Status { get; set; } + + /// User-facing warning produced while applying the model switch. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Identifier of the completed or cancelled task to remove from tracking. +/// Filesystem and environment context used to resolve model-picker settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksRemoveRequest +public sealed class ModelPickerSettingsContext { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Optional Copilot configuration directory containing persisted settings. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// 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; } -/// Indicates whether the message was delivered, with an error message when delivery failed. +/// RPC data type for ModelPickerPersistence operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksSendMessageResult +public sealed class ModelPickerPersistenceRequest { - /// Error message if delivery failed. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Whether context tier was explicitly selected and should be persisted. + [JsonPropertyName("contextTierExplicit")] + public bool? ContextTierExplicit { get; set; } - /// Whether the message was successfully delivered or steered. - [JsonPropertyName("sent")] - public bool Sent { get; set; } + /// Whether reasoning effort was explicitly selected and should be persisted. + [JsonPropertyName("reasoningEffortExplicit")] + public bool? ReasoningEffortExplicit { get; set; } + + /// Filesystem and environment context used to resolve settings persistence. + [JsonPropertyName("settingsContext")] + public ModelPickerSettingsContext SettingsContext { get => field ??= new(); set; } } -/// Identifier of the target agent task, message content, and optional sender agent ID. +/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksSendMessageRequest +public sealed class ModelSwitchToRequest { - /// Agent ID of the sender, if sent on behalf of another agent. - [JsonPropertyName("fromAgentId")] - public string? FromAgentId { get; 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; } - /// Agent task identifier. - [JsonPropertyName("id")] - public string Id { 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; } - /// Message content to send to the agent. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// 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; } - /// 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; } -/// Skill metadata available to a session, with name, description, source, enabled/invocable state, path, plugin, and argument hint. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Skill -{ - /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. - [JsonPropertyName("argumentHint")] - public string? ArgumentHint { get; set; } + /// Override individual model capabilities resolved by the runtime. + [JsonPropertyName("modelCapabilities")] + public ModelCapabilitiesOverride? ModelCapabilities { get; set; } - /// Canonical slash command name used to invoke the skill, without the leading '/'. - [JsonPropertyName("commandName")] - public string? CommandName { get; set; } + /// Settings scope used when persisting the selected model. + [JsonPropertyName("modelChangeScope")] + public string? ModelChangeScope { get; set; } - /// Description of what the skill does. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// 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; } - /// Whether the skill is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Optional settings context and explicit-override flags used to persist a picker selection. + [JsonPropertyName("pickerPersistence")] + public ModelPickerPersistenceRequest? PickerPersistence { get; set; } - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { 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; } - /// Absolute path to the skill file. - [JsonPropertyName("path")] - public string? Path { 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; } - /// Name of the plugin that provides the skill, when source is 'plugin'. - [JsonPropertyName("pluginName")] - public string? PluginName { get; set; } + /// Reasoning summary mode to request for supported model clients. + [JsonPropertyName("reasoningSummary")] + public ReasoningSummary? ReasoningSummary { get; set; } - /// Source location type (e.g., project, personal-copilot, plugin, builtin). + /// 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; } + + /// 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 SkillSource Source { get; set; } + public ModelChangeSource? Source { get; set; } - /// Whether the skill can be invoked by the user as a slash command. - [JsonPropertyName("userInvocable")] - public bool UserInvocable { get; set; } + /// Output verbosity level to request for supported models. + [JsonPropertyName("verbosity")] + public Verbosity? Verbosity { get; set; } } -/// Skills available to the session, with their enabled state. +/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillList +internal sealed class ModelSwitchToRequestWithSession { - /// Available skills. - [JsonPropertyName("skills")] - public IList Skills { 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; } + + /// 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; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsListRequest -{ /// 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; } } -/// Skill invocation record with name, path, content, allowed tools, and turn number. +/// 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 SkillsInvokedSkill +public sealed class ModelSwitchAutoTierResult { - /// Tools that should be auto-approved when this skill is active, captured at invocation time. - [JsonPropertyName("allowedTools")] - public IList? AllowedTools { get; set; } - - /// Full content of the skill file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// 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; } - /// Whether model invocation was disabled when this skill was invoked. - [JsonPropertyName("disableModelInvocation")] - public bool? DisableModelInvocation { get; set; } + /// Auto preference currently committed for the session. + [JsonPropertyName("effectiveAutoTier")] + public AutoTier? EffectiveAutoTier { get; set; } - /// Turn number when the skill was invoked. - [JsonPropertyName("invokedAtTurn")] - public long InvokedAtTurn { get; set; } + /// Latest unclaimed Auto preference waiting for a future user turn. + [JsonPropertyName("pendingAutoTier")] + public AutoTier? PendingAutoTier { get; set; } - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Immediate request status. `pending` means accepted but not committed. + [JsonPropertyName("status")] + public ModelSwitchAutoTierStatus Status { 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; + /// 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; } } -/// Skills invoked during this session, ordered by invocation time (most recent last). +/// 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)] -public sealed class SkillsGetInvokedResult +internal sealed class ModelSwitchAutoTierRequest { - /// Skills invoked during this session, ordered by invocation time (most recent last). - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; set; } -} + /// 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; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsGetInvokedRequest -{ /// 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; } } -/// Name of the skill to enable for the session. +/// Managed, repository, and CLI model overrides to overlay onto the session at startup. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsEnableRequest +internal sealed class ModelApplyStartupOverlayRequest { - /// Name of the skill to enable. - [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; } -/// Name of the skill to disable for the session. +/// RPC data type for SessionModelClearStartupSeed operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsDisableRequest +internal sealed class SessionModelClearStartupSeedResult { - /// Name of the skill to disable. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } -/// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. +/// The applied host allowlist and effective session model policy after intersection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsLoadDiagnostics +public sealed class ModelSetAllowedModelsResult { - /// Errors emitted while loading skills (e.g. skills that failed to load entirely). - [JsonPropertyName("errors")] - public IList Errors { 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; } - /// Warnings emitted while loading skills (e.g. skills that loaded but had issues). - [JsonPropertyName("warnings")] - public IList Warnings { 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; } + + /// 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 SessionSkillsReloadRequest +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; } -/// Identifies the target session. +/// 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)] -internal sealed class SessionSkillsEnsureLoadedRequest +public sealed class ModelSetReasoningEffortResult +{ + /// Reasoning effort level recorded on the session after the update. + [JsonPropertyName("reasoningEffort")] + public string ReasoningEffort { get; set; } = string.Empty; +} + +/// Reasoning effort level to apply to the currently selected model. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ModelSetReasoningEffortRequest { + /// 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; } -/// The IDE a host is connected to, as reported to the session. +/// Reasoning-summaries presentation policy for the session's current model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionConnectedIdeInfo +public sealed class ModelGetReasoningSummariesResult { - /// Display name of the connected IDE, for example `VS Code`. - [JsonPropertyName("ideName")] - public string IdeName { 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; } - /// Absolute path of the workspace folder the IDE has open. - [JsonPropertyName("workspaceFolder")] - public string WorkspaceFolder { get; set; } = string.Empty; + /// 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; } + + /// Whether the selected model shows reasoning summaries by default. + [JsonPropertyName("modelDefaultShowsSummaries")] + public bool ModelDefaultShowsSummaries { 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; } + + /// Whether turns on this session produce reasoning summaries, honouring an explicit session reasoning-summary override. + [JsonPropertyName("sessionShowsSummaries")] + public bool SessionShowsSummaries { 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 selected model emits reasoning headers. + [JsonPropertyName("showReasoningHeaders")] + public bool ShowReasoningHeaders { 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; } } -/// Records which IDE the host is connected to, or clears it. +/// Host presentation state the reasoning-summaries policy depends on. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpSetConnectedIdeInfoParams +internal sealed class ModelGetReasoningSummariesRequest { - /// 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; } + /// The host's persisted `showReasoning` user setting, omitted when unset. + [JsonPropertyName("persistedShowReasoning")] + public bool? PersistedShowReasoning { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Whether the host is currently showing reasoning summaries. Defaults to false. + [JsonPropertyName("summariesVisible")] + public bool? SummariesVisible { get; set; } } -/// Recorded MCP server connection failure. +/// Availability of a server-advertised routing preference. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerFailureInfo +public sealed class AutoTierStatus { - /// Failure message produced when the MCP server connection failed. + /// 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; } = string.Empty; + public string? Message { get; set; } - /// epoch-ms timestamp at which the failure was recorded. - [JsonPropertyName("timestamp")] - public long Timestamp { get; set; } + /// Extensible machine-readable unavailability reason. + [JsonPropertyName("reason")] + public string? Reason { get; set; } } -/// Recorded MCP server pending-auth state. +/// A server-advertised routing preference. Identifiers and execution types are extensible. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerNeedsAuthInfo +public sealed class AutoTierDescriptor { - /// epoch-ms timestamp at which the server signalled it needs authentication. - [JsonPropertyName("timestamp")] - public long Timestamp { get; set; } -} + /// Description displayed beside the preference. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; -/// Host-level state, omitted when no MCP host is initialized. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpHostState -{ - /// Names of currently-connected MCP clients. - [JsonPropertyName("clients")] - public IList Clients { get => field ??= []; set; } + /// Human-readable label, not a routing identifier. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Configured servers that are explicitly disabled. - [JsonPropertyName("disabledServers")] - public IList DisabledServers { get => field ??= []; set; } + /// Opaque routing identifier transmitted unchanged to the provider. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Map of server name to recorded connection failure. - [JsonPropertyName("failedServers")] - public IDictionary FailedServers { get => field ??= new Dictionary(); set; } + /// Current account-specific availability. + [JsonPropertyName("status")] + public AutoTierStatus Status { get => field ??= new(); set; } - /// Configured servers filtered out by MCP server policy. - [JsonPropertyName("filteredServers")] - public IList FilteredServers { get => field ??= []; set; } + /// Execution kind; this client supports `auto` preferences on the Auto model. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; +} - /// Whether third-party MCP servers are policy-enabled for this session. - [JsonPropertyName("mcp3pEnabled")] - public bool Mcp3pEnabled { 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; - /// Map of server name to recorded pending-auth state. - [JsonPropertyName("needsAuthServers")] - public IDictionary NeedsAuthServers { get => field ??= new Dictionary(); set; } + /// Provider that supplied this metadata, when the catalog is provider-attributed. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } - /// Names of servers with in-flight connection attempts. - [JsonPropertyName("pendingConnections")] - public IList PendingConnections { get => field ??= []; set; } + /// Routing preferences in the server's presentation order. + [JsonPropertyName("tiers")] + public IList Tiers { get => field ??= []; set; } } -/// Owned installation that a listed MCP server's live configuration came from. +/// Cost-category metadata for a CAPI model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerOwnership +public sealed class SessionModelPriceCategory { - /// Stable installation identifier from the owned installation receipt. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; + /// CAPI model identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Cost category assigned to the model. + [JsonPropertyName("priceCategory")] + public ModelPickerPriceCategory PriceCategory { get; set; } } -/// MCP server status entry, including config source/plugin source and any connection error. +/// 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 McpServer +public sealed class ModelProviderDescriptor { - /// Human-readable display name supplied by a managed server catalog. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Opaque, stable provider id, stamped onto every model this provider returns. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Error message if the server failed to connect. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// The neutral provider kind. + [JsonPropertyName("kind")] + public ModelProviderKind Kind { 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; + /// Human-readable menu label, owned by the runtime so every consumer renders identical text. + [JsonPropertyName("label")] + public string Label { 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; } + /// Stable ordering key for presenting providers in a deterministic sequence. + [JsonPropertyName("ordering")] + public long Ordering { 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 list of models available to this session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionModelList +{ + /// Ordered Auto routing preferences discovered for this session's account. + [JsonPropertyName("auto")] + public AutoTierMetadata? Auto { get; set; } - /// Configuration source: user, workspace, plugin, builtin, or managed. - [JsonPropertyName("source")] - public McpServerSource? Source { 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; } - /// Plugin name that provided this server, when source is plugin. - [JsonPropertyName("sourcePlugin")] - public string? SourcePlugin { 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; } - /// Plugin version that provided this server, when source is plugin. - [JsonPropertyName("sourcePluginVersion")] - public string? SourcePluginVersion { 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; } - /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. - [JsonPropertyName("status")] - public McpServerStatus Status { get; set; } + /// Per-quota snapshots returned alongside the model list, keyed by quota type. + [JsonPropertyName("quotaSnapshots")] + public IDictionary? QuotaSnapshots { 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; } +/// 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; } } -/// MCP servers configured for the session, with their connection status and host-level state. +/// RPC data type for SessionModelListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerList +internal sealed class SessionModelListRequestWithSession { - /// Host-level state, omitted when no MCP host is initialized. - [JsonPropertyName("host")] - public McpHostState? Host { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Configured MCP servers. - [JsonPropertyName("servers")] - public IList Servers { get => field ??= []; set; } + /// If true, bypasses the per-session model list cache and re-fetches from CAPI. + [JsonPropertyName("skipCache")] + public bool? SkipCache { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpListRequest +internal sealed class SessionModeGetRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Observational state for a matching already materialized MCP server. +/// 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 McpConfiguredServerState +public sealed class ModeSetResult { - /// Observed connection error, when the materialized server failed. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Whether the host should arm an interactive continuation after the mode change. + [JsonPropertyName("armInteractiveContinuation")] + public bool? ArmInteractiveContinuation { get; set; } - /// Observed connection status. This is not a configuration or readiness guarantee. + /// 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 McpServerStatus Status { get; set; } + public string Status { get; set; } = string.Empty; + + /// User-facing warning produced while applying the mode change. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Effective MCP configuration entry. Configuration enablement is distinct from the optional live observation. +/// Agent interaction mode to apply to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpConfiguredServer +internal sealed class ModeSetRequest { - /// Human-readable display name supplied by configuration. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Explicit response to a model-switch compaction preflight. + [JsonPropertyName("compactionDecision")] + public string? CompactionDecision { get; set; } - /// Whether this configured server is enabled after session configuration and policy filtering. - [JsonPropertyName("enabled")] - public bool Enabled { 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; } - /// 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; } + /// Session whose plan-mode base state should be inherited. + [JsonPropertyName("inheritPlanBaseFromSessionId")] + public string? InheritPlanBaseFromSessionId { 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; + /// The session mode the agent is operating in. + [JsonPropertyName("mode")] + public SessionMode Mode { get; set; } - /// Configuration provenance: user, workspace, plugin, builtin, or managed. - [JsonPropertyName("source")] - public McpServerSource? Source { get; set; } + /// Whether the selected plan model should be persisted. + [JsonPropertyName("persistPlanSelection")] + public bool? PersistPlanSelection { get; set; } - /// Plugin name that provided this server, when source is plugin. - [JsonPropertyName("sourcePlugin")] - public string? SourcePlugin { get; set; } + /// Settings context used when persisting the selected plan model. + [JsonPropertyName("pickerSettingsContext")] + public ModelPickerSettingsContext? PickerSettingsContext { get; set; } - /// Plugin version that provided this server, when source is plugin. - [JsonPropertyName("sourcePluginVersion")] - public string? SourcePluginVersion { 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; } + + /// Provider owning planModel. Omit for legacy bare-model selection. + [JsonPropertyName("planModelProviderId")] + public string? PlanModelProviderId { 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; } -/// Effective MCP configuration with optional live observations from matching already materialized servers. +/// The session's friendly name, or null when not yet set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpConfiguredServerList +public sealed class NameGetResult { - /// Effective configured MCP servers. - [JsonPropertyName("servers")] - public IList Servers { 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 SessionMcpListConfiguredRequest +internal sealed class SessionNameGetRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpToolUi -{ - /// 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; } -} - -/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. +/// New friendly name to apply to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpTools +internal sealed class NameSetRequest { - /// Tool description, when provided. - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// Tool name. + /// 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; - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Tools exposed by the connected MCP server. Throws when the server is not connected. +/// Indicates whether the auto-generated summary was applied as the session's name. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpListToolsResult +public sealed class NameSetAutoResult { - /// Tools exposed by the server. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// 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; } } -/// Server name whose tool list should be returned. +/// 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 McpListToolsRequest +internal sealed class NameSetAutoRequest { - /// 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; - /// 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; } -/// Name of the MCP server to enable for the session. +/// Existence, contents, and resolved path of the session plan file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpEnableRequest +public sealed class PlanReadResult { - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// The content of the plan file, or null if it does not exist. + [JsonPropertyName("content")] + public string? Content { 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; } + /// Whether the plan file exists in the workspace. + [JsonPropertyName("exists")] + public bool Exists { get; set; } + + /// Absolute file path of the plan file, or null if workspace is not enabled. + [JsonPropertyName("path")] + public string? Path { get; set; } } -/// Name of the MCP server to enable for the session. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpEnableRequestWithSession +internal sealed class SessionPlanReadRequest { - /// 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; } -/// Name of the MCP server to disable for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDisableRequest -{ - /// 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)] - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} - -/// Name of the MCP server to disable for the session. +/// Replacement contents to write to the session plan file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpDisableRequestWithSession +internal sealed class PlanUpdateRequest { - /// 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)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// The new content for the plan file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] @@ -17100,2330 +16738,2143 @@ internal sealed class McpDisableRequestWithSession /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpReloadRequest +internal sealed class SessionPlanDeleteRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of moving in-flight MCP loading to the background. +/// 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 MoveMcpLoadingToBackgroundResult +public sealed class PlanSqlTodosRow { - /// 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; } + /// 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; } + + /// Todo identifier. + [JsonPropertyName("id")] + public string? Id { get; set; } + + /// Todo status. + [JsonPropertyName("status")] + public string? Status { get; set; } + + /// Todo title. + [JsonPropertyName("title")] + public string? Title { get; set; } +} + +/// Todo rows read from the session SQL database. Empty when no session database is available. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PlanReadSqlTodosResult +{ + /// 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; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpMoveLoadingToBackgroundRequest +internal sealed class SessionPlanReadSqlTodosRequest { /// 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. +/// 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 McpAllowedServer +public sealed class PlanSqlTodoDependency { - /// Allowed server name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// ID of the todo it depends on. + [JsonPropertyName("dependsOn")] + public string DependsOn { get; set; } = string.Empty; - /// PII-free note explaining why the server was allowed. - [JsonPropertyName("redactedNote")] - public string? RedactedNote { get; set; } + /// ID of the todo that has the dependency. + [JsonPropertyName("todoId")] + public string TodoId { get; set; } = string.Empty; } -/// MCP server whose connection attempt failed. +/// Todo rows + dependency edges read from the session SQL database. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpFailedServer +public sealed class PlanReadSqlTodosWithDependenciesResult { - /// 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; + /// 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; } } -/// MCP server filtered by policy, with name, reason, and optional redacted reason. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpFilteredServer +internal sealed class SessionPlanReadSqlTodosWithDependenciesRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Filtered server name. +/// RPC data type for WorkspacesGetWorkspaceResultWorkspace operations. +public sealed class WorkspacesGetWorkspaceResultWorkspace +{ + /// Current Git branch. + [JsonPropertyName("branch")] + public string? Branch { get; set; } + + /// Whether the per-session Chronicle upgrade prompt was dismissed for the workspace. + [JsonPropertyName("chronicle_sync_dismissed")] + public bool? ChronicleSyncDismissed { get; set; } + + /// Name of the client that created the workspace. + [JsonPropertyName("client_name")] + public string? ClientName { get; set; } + + /// Timestamp when the workspace was created. + [JsonPropertyName("created_at")] + public DateTimeOffset? CreatedAt { get; set; } + + /// Current working directory associated with the workspace. + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } + + /// Git repository root associated with the workspace. + [JsonPropertyName("git_root")] + public string? GitRoot { get; set; } + + /// Allowed values for the `WorkspacesWorkspaceDetailsHostType` enumeration. + [JsonPropertyName("host_type")] + public WorkspacesWorkspaceDetailsHostType? HostType { get; 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; + + /// Most recent Mission Control event identifier observed for the workspace. + [JsonPropertyName("mc_last_event_id")] + public string? McLastEventId { get; set; } + + /// Mission Control session identifier associated with the workspace. + [JsonPropertyName("mc_session_id")] + public string? McSessionId { get; set; } + + /// Mission Control task identifier associated with the workspace. + [JsonPropertyName("mc_task_id")] + public string? McTaskId { get; set; } + + /// Workspace display name. [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public string? Name { get; set; } - /// Human-readable filter reason. - [JsonPropertyName("reason")] - public string Reason { get; set; } = string.Empty; + /// Whether the workspace session can be steered remotely. + [JsonPropertyName("remote_steerable")] + public bool? RemoteSteerable { get; set; } - /// PII-free filter reason. - [JsonPropertyName("redactedReason")] - public string? RedactedReason { 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; } } -/// MCP server startup filtering result. +/// Current workspace metadata for the session, including its absolute filesystem path when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStartServersResult +public sealed class WorkspacesGetWorkspaceResult { - /// 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; } + /// 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; } - /// Servers filtered out before startup. - [JsonPropertyName("filteredServers")] - public IList FilteredServers { get => field ??= []; set; } + /// Current workspace metadata, or null if not available. + [JsonPropertyName("workspace")] + public WorkspacesGetWorkspaceResultWorkspace? Workspace { get; set; } } -/// Opaque MCP reload configuration. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpReloadWithConfigRequest +internal sealed class SessionWorkspacesGetWorkspaceRequest { /// 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. +/// Workspace metadata fields to update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpExecuteSamplingResult +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; } -/// Outcome of an MCP sampling execution: success result, failure error, or cancellation. +/// Optional session context used when creating a local workspace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSamplingExecutionResult +internal sealed class WorkspacesEnsureRequest { - /// 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; } + /// Opaque workspace context supplied by the session host. + [JsonPropertyName("context")] + public JsonElement? Context { 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. +/// Relative paths of files stored in the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpExecuteSamplingRequest +public sealed class WorkspacesListFilesResult { + /// Slash-separated relative file paths in the workspace files directory. + [JsonPropertyName("files")] + public IList Files { get => field ??= []; set; } } -/// Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpExecuteSamplingParams +internal sealed class SessionWorkspacesListFilesRequest { - /// 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; - - /// Name of the MCP server that initiated the sampling request. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether an in-flight sampling execution with the given requestId was found and cancelled. +/// Contents of the requested workspace file as a UTF-8 string. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpCancelSamplingExecutionResult +public sealed class WorkspacesReadFileResult { - /// 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; } + /// File content as a UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; } -/// The requestId previously passed to executeSampling that should be cancelled. +/// Relative path of the workspace file to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpCancelSamplingExecutionParams +internal sealed class WorkspacesReadFileRequest { - /// The requestId previously passed to executeSampling that should be cancelled. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { 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. +/// Relative path and UTF-8 content for the workspace file to create or overwrite. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSetEnvValueModeResult +internal sealed class WorkspacesCreateFileRequest { - /// Mode recorded on the session after the update. - [JsonPropertyName("mode")] - public McpSetEnvValueModeDetails Mode { get; set; } -} + /// File content to write as a UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; -/// 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; } + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove). +/// Filesystem metadata for a path in the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpRemoveGitHubResult +public sealed class WorkspacesStatFileResult { - /// 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; } + /// Creation time in Unix epoch milliseconds. + [JsonPropertyName("birthtimeMs")] + public double BirthtimeMs { get; set; } + + /// Whether the path identifies a directory. + [JsonPropertyName("isDirectory")] + public bool IsDirectory { get; set; } + + /// Whether the path identifies a regular file. + [JsonPropertyName("isFile")] + public bool IsFile { get; set; } + + /// Last modification time in Unix epoch milliseconds. + [JsonPropertyName("mtimeMs")] + public double MtimeMs { get; set; } + + /// Size in bytes. + [JsonPropertyName("size")] + public double Size { get; set; } } -/// Identifies the target session. +/// Relative path of the workspace file or directory to inspect. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpRemoveGitHubRequest +internal sealed class WorkspacesStatFileRequest { + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of configuring GitHub MCP. +/// Directory to create within the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigureGitHubResult +internal sealed class WorkspacesCreateDirectoryRequest { - /// Whether GitHub MCP configuration changed. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// 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; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Credential-free authentication identity used to configure GitHub MCP. +/// File or directory to remove from the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigureGitHubRequest +internal sealed class WorkspacesRemovePathRequest { + /// 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; } -/// 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. +/// Source and destination paths for a rename within the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpStartServerRequest +internal sealed class WorkspacesRenamePathRequest { - /// 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; } + /// Slash-separated destination path relative to the workspace files directory. + [JsonPropertyName("destination")] + public string Destination { get; set; } = string.Empty; - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Name of the MCP server to start. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Slash-separated source path relative to the workspace files directory. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; } -/// 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. +/// Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStartServerRequestWithSession +public sealed class WorkspacesCheckpoints { - /// 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; } + /// Filename of the checkpoint within the workspace checkpoints directory. + [JsonPropertyName("filename")] + public string Filename { get; set; } = string.Empty; - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Checkpoint number assigned by the workspace manager. + [JsonPropertyName("number")] + public long Number { get; set; } - /// Name of the MCP server to start. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// 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 SessionWorkspacesListCheckpointsRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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. +/// 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 McpRestartServerRequest +public sealed class WorkspacesReadCheckpointResult { - /// 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; } - - /// 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 required string ServerName { 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; } } -/// 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. +/// Checkpoint number to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRestartServerRequestWithSession +internal sealed class WorkspacesReadCheckpointRequest { - /// 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; } - - /// 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; + /// Checkpoint number to read. + [JsonPropertyName("number")] + public long Number { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Server name for an individual MCP server stop. +/// Persisted summary metadata and refreshed workspace metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpStopServerRequest +public sealed class WorkspacesAddSummaryResult { - /// Exact owned receipt identity. Stop also forgets this session's durable activation. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Metadata for the persisted summary. + [JsonPropertyName("summary")] + public IDictionary? Summary { get; set; } - /// Name of the MCP server to stop. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Refreshed metadata for the containing workspace. + [JsonPropertyName("workspace")] + public IDictionary? Workspace { get; set; } } -/// Server name for an individual MCP server stop. +/// Compaction summary checkpoint to persist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStopServerRequestWithSession +internal sealed class WorkspacesAddSummaryRequest { - /// 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; + /// 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; } -/// Registration parameters for an external MCP client. +/// Rollback point for local workspace summaries. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRegisterExternalClientRequest +internal sealed class WorkspacesTruncateSummariesRequest { - /// Logical server name for the external client. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// 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; } -/// Server name identifying the external client to remove. +/// Autopilot objective file content, or null when missing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpUnregisterExternalClientRequest +public sealed class WorkspacesReadAutopilotObjectiveResult { - /// Server name of the external client to unregister. - [JsonPropertyName("serverName")] - public string ServerName { 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; } -/// Whether the named MCP server is running. +/// Result of writing the autopilot objective file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpIsServerRunningResult +public sealed class WorkspacesWriteAutopilotObjectiveResult { - /// True if the server has an active client and transport. - [JsonPropertyName("running")] - public bool Running { get; set; } + /// Filesystem operation performed. + [JsonPropertyName("operation")] + public string Operation { get; set; } = string.Empty; } -/// Server name to check running status for. +/// Autopilot objective file content to persist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpIsServerRunningRequest +internal sealed class WorkspacesWriteAutopilotObjectiveRequest { - /// Name of the MCP server to check. - [JsonPropertyName("serverName")] - public string ServerName { 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 pending MCP OAuth response was accepted. +/// Result of deleting the autopilot objective file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthHandlePendingResult +public sealed class WorkspacesDeleteAutopilotObjectiveResult { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// True when a file was deleted. + [JsonPropertyName("deleted")] + public bool Deleted { get; set; } } -/// Host response to the pending OAuth request. -/// Polymorphic base type discriminated by kind. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpOauthPendingRequestResponseToken), "token")] -[JsonDerivedType(typeof(McpOauthPendingRequestResponseCancelled), "cancelled")] -public partial class McpOauthPendingRequestResponse +internal sealed class SessionWorkspacesDeleteAutopilotObjectiveRequest { - /// 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 token variant of . +/// Whether the autopilot objective file exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthPendingRequestResponseToken : McpOauthPendingRequestResponse +public sealed class WorkspacesAutopilotObjectiveExistsResult { - /// - [JsonIgnore] - public override string Kind => "token"; + /// True when the objective file exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } +} - /// Access token acquired by the SDK host. - [JsonPropertyName("accessToken")] - public required string AccessToken { 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; +} - /// Token lifetime in seconds, if known. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("expiresIn")] - public long? ExpiresIn { 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; - /// OAuth token type. Defaults to bearer when omitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("tokenType")] - public string? TokenType { get; set; } -} + /// Absolute filesystem path to the saved paste file. + [JsonPropertyName("filePath")] + public string FilePath { get; set; } = string.Empty; -/// The cancelled variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthPendingRequestResponseCancelled : McpOauthPendingRequestResponse -{ - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// Size of the saved file in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } } -/// Pending MCP OAuth request ID and host-provided token or cancellation response. +/// Descriptor for the saved paste file, or null when the workspace is unavailable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthHandlePendingRequest +public sealed class WorkspacesSaveLargePasteResult { - /// 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; + /// 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 MCP server whose persisted OAuth credentials were updated. +/// Pasted content to save as a UTF-8 file in the session workspace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthAuthenticationStateChangedRequest +internal sealed class WorkspacesSaveLargePasteRequest { - /// 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; } + /// 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; } -/// An inert runtime-issued login handle. Preparation alone performs no activation or OAuth work. +/// 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 SessionMcpOauthPrepareLoginResult +public sealed class WorkspaceDiffContents { - /// Original expiry, not extended by consumption, retries or cancellation. - [JsonPropertyName("expiresAt")] - public DateTimeOffset ExpiresAt { 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; } - /// Retain with the original requester and use for one login or cancellation. - [JsonPropertyName("loginId")] - public string LoginId { 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; } } -/// Effect-free preparation bound to the existing local session, requester and installation, with frozen options. +/// A single changed file and its unified diff. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpOauthPrepareLoginRequest +public sealed class WorkspaceDiffFileChange { - /// 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; } + /// Type of change represented by this file diff. + [JsonPropertyName("changeType")] + public WorkspaceDiffFileChangeType ChangeType { 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; } + /// 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; } - /// Exact installation identity from owned inventory, never a server-name alias. - [JsonPropertyName("expectedInstallationId")] - public string ExpectedInstallationId { 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; - /// Request a new authorisation rather than accepting a usable cached grant. - [JsonPropertyName("forceReauth")] - public bool? ForceReauth { get; set; } + /// Whether the diff content was omitted because it exceeded the per-file size limit. + [JsonPropertyName("isTruncated")] + public bool? IsTruncated { get; set; } - /// Name recorded by the authoritative owned installation receipt. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Original file path for renamed files. + [JsonPropertyName("oldPath")] + public string? OldPath { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } -/// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. +/// Workspace diff result for the requested mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthLoginResult +public sealed class WorkspaceDiffResult { - /// 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; } + /// Default branch used for a branch diff, when branch mode was requested. + [JsonPropertyName("baseBranch")] + public string? BaseBranch { 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; } + /// Changed files and their unified diffs. + [JsonPropertyName("changes")] + public IList Changes { get => field ??= []; set; } - /// Runtime-issued owned flow identity; never a server name or installation operation ID. - [JsonPropertyName("loginId")] - public string? LoginId { get; 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; } - /// Explicit outcome for owned sign-in. Manual callers retain their legacy response shape. - [JsonPropertyName("status")] - public McpOwnedOauthLoginStatus? Status { 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; } } -/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. +/// Parameters for computing a workspace diff. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthLoginRequest +public sealed class WorkspacesDiffRequest { - /// 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, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + [JsonPropertyName("ignoreWhitespace")] + public bool? IgnoreWhitespace { 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; } + /// 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; } - /// 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; } + /// Diff mode requested by the client. + [JsonPropertyName("mode")] + public required WorkspaceDiffMode Mode { 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; } +/// Parameters for computing a workspace diff. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesDiffRequestWithSession +{ + /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + [JsonPropertyName("ignoreWhitespace")] + public bool? IgnoreWhitespace { 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; } + /// 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; } - /// 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; } + /// Diff mode requested by the client. + [JsonPropertyName("mode")] + public WorkspaceDiffMode Mode { 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. +/// Current per-window credit limit and consumption for an autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthLoginRequestWithSession +public sealed class AutopilotObjectiveCreditLimit { - /// 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; } + /// Configured AI-credit cap, when one is set. + [JsonPropertyName("credits")] + public double? Credits { 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; } + /// Window consumption in fractional AI credits, for display. + [JsonPropertyName("creditsUsed")] + public double CreditsUsed { 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 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; +} - /// Exact owned receipt identity. Owned login never uses an implicit helper session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } +/// Public, persistence-independent projection of an autopilot objective. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class AutopilotObjectiveState +{ + /// Optional summary recorded when the objective completed. + [JsonPropertyName("completionSummary")] + public string? CompletionSummary { 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; } + /// 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; - /// 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; } + /// Current per-window consumption and optional cap, when a credit-tracking window is present. + [JsonPropertyName("creditLimit")] + public AutopilotObjectiveCreditLimit? CreditLimit { 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; } + /// Session-local objective identifier. + [JsonPropertyName("id")] + public long Id { 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; } + /// User-provided objective text. + [JsonPropertyName("objective")] + public string Objective { get; set; } = string.Empty; - /// 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; } + /// Optional reason the objective is paused. + [JsonPropertyName("pauseReason")] + public string? PauseReason { 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; + /// Current normalized lifecycle status. + [JsonPropertyName("status")] + public AutopilotObjectiveStatus Status { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Number of objective turns started. + [JsonPropertyName("turnCount")] + public long TurnCount { get; set; } } -/// Host-delivered callback for a runtime-managed MCP OAuth login. +/// Canonical runtime state for the session's current autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthCompleteRequest +public sealed class AutopilotObjectiveGetStateResult { - /// 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; + /// Current objective state, or `null` when the session has no objective. + [JsonPropertyName("state")] + public AutopilotObjectiveState? State { get; set; } } -/// 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. +/// Identifies the target session. [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 +internal sealed class SessionAutopilotObjectiveGetStateRequest { - /// The type discriminator. - [JsonPropertyName("status")] - public virtual string Status { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } - -/// The no-auth-required variant of . +/// 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 partial class McpOauthProbeResultNoAuthRequired : McpOauthProbeResult +public sealed class CompletionsGetTriggerCharactersResult { - /// - [JsonIgnore] - public override string Status => "no-auth-required"; - - /// HTTP response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// Trigger characters advertised by the host (e.g. `["@", "#"]`). Empty disables host-driven completions for the session. + [JsonPropertyName("triggerCharacters")] + public IList TriggerCharacters { get => field ??= []; set; } } -/// The authenticated variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultAuthenticated : McpOauthProbeResult +internal sealed class SessionCompletionsGetTriggerCharactersRequest { - /// - [JsonIgnore] - public override string Status => "authenticated"; - - /// HTTP response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The needs-auth variant of . +/// 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 partial class McpOauthProbeResultNeedsAuth : McpOauthProbeResult +public sealed class SessionCompletionItem { - /// - [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; } + /// Text spliced into the composer when the item is accepted. + [JsonPropertyName("insertText")] + public string InsertText { get; set; } = string.Empty; - /// Parsed WWW-Authenticate challenge parameters, when present and parseable. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("wwwAuthenticateParams")] - public McpOauthWWWAuthenticateParams? WwwAuthenticateParams { 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; } -/// The failed variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultFailed : McpOauthProbeResult -{ - /// - [JsonIgnore] - public override string Status => "failed"; + /// Primary display label for the picker row. Falls back to `insertText` when absent. + [JsonPropertyName("label")] + public string? Label { get; set; } - /// Human-readable probe failure detail. - [JsonPropertyName("error")] - public required string Error { get; set; } + /// End (exclusive) of the replacement range in `text`, in UTF-16 code units. + [JsonPropertyName("rangeEnd")] + public long? RangeEnd { 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; } + /// Start of the replacement range in `text`, in UTF-16 code units. + [JsonPropertyName("rangeStart")] + public long? RangeStart { get; set; } } -/// Remote MCP server name for a passive OAuth status probe. +/// 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 McpOauthProbeRequest +public sealed class CompletionsRequestResult { - /// 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; } + /// Completion items in host-ranked order. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } } -/// Remote MCP server name for a passive OAuth status probe. +/// Request host-driven completions for the current composer input. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthProbeRequestWithSession +internal sealed class CompletionsRequestRequest { - /// 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; + /// Cursor offset within `text`, in UTF-16 code units. + [JsonPropertyName("offset")] + public long Offset { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// The full composed composer input. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; } -/// Honest terminal cancellation result; persistence or recovery failures remain RPC errors. +/// Instruction sources loaded for the session, in merge order. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMcpOauthCancelLoginResult +public sealed class InstructionsGetSourcesResult { - /// True after cancellation settles, false when the original login already connected successfully. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// Instruction sources for the session. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; 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 SessionInstructionsGetSourcesRequest { - /// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthRespondResult +internal sealed class SessionInstructionsReloadRequest { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Pending MCP OAuth request id to respond to. +/// Result of one customization reload component. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthRespondRequest +public sealed class CustomizationReloadOutcome { - /// 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; -} + /// Reason for a skipped component or description of a failure, when available. + [JsonPropertyName("detail")] + public string? Detail { get; set; } -/// 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; } -} + /// Whether the component reloaded, was skipped, or failed. + [JsonPropertyName("status")] + public CustomizationReloadStatus Status { 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; + /// Component whose reload was attempted or skipped. + [JsonPropertyName("subsystem")] + public CustomizationReloadSubsystem Subsystem { get; set; } } - -/// The headers variant of . +/// 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 partial class McpHeadersHandlePendingHeadersRefreshRequestHeaders : McpHeadersHandlePendingHeadersRefreshRequest +public sealed class CustomizationsReloadResult { - /// - [JsonIgnore] - public override string Kind => "headers"; + /// Errors from any component that could not be refreshed. + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } - /// 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; } + /// Outcome of each component in reload order; a skipped component was not configured or loaded. + [JsonPropertyName("outcomes")] + public IList Outcomes { get => field ??= []; 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; } + /// Warnings from skill discovery. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// The none variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestNone : McpHeadersHandlePendingHeadersRefreshRequest +internal sealed class SessionCustomizationsReloadRequest { - /// - [JsonIgnore] - public override string Kind => "none"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The error variant of . +/// Indicates whether fleet mode was successfully activated. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestError : McpHeadersHandlePendingHeadersRefreshRequest +public sealed class FleetStartResult { - /// - [JsonIgnore] - public override string Kind => "error"; - - /// Host credential broker failure, denial, or revocation reason. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Whether fleet mode was successfully activated. + [JsonPropertyName("started")] + public bool Started { get; set; } } -/// MCP headers refresh request id and the host response. +/// 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 McpHeadersHandlePendingHeadersRefreshRequestRequest +internal sealed class FleetStartRequest { - /// Headers refresh request identifier from mcp.headers_refresh_required. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the fleet request. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } - /// Host response: supply dynamic headers or decline this refresh. - [JsonPropertyName("result")] - public McpHeadersHandlePendingHeadersRefreshRequest Result { get => field ??= new(); 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; } + + /// 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; + + /// If true, await completion of the agentic loop for this fleet request before returning. Defaults to false. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// MCP Apps resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +/// Agents available to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsResourceContent +public sealed class AgentList { - /// 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; + /// Available agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// Resource contents returned by the MCP server. +/// RPC data type for SessionAgentList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsReadResourceResult +public sealed class SessionAgentListRequest { - /// Resource contents returned by the server. - [JsonPropertyName("contents")] - public IList Contents { get => field ??= []; 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; } + + /// 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; } } -/// MCP server and resource URI to fetch. +/// RPC data type for SessionAgentListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsReadResourceRequest +internal sealed class SessionAgentListRequestWithSession { - /// 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; + /// 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, 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; - - /// 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. +/// An in-memory authored prompt override for an available agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsListToolsRequest +internal sealed class AgentSetPromptRequest { - /// **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; + /// Stable effective agent id. Plugin namespace separators are normalized. + [JsonPropertyName("id")] + public string Id { 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; + /// 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; } -/// MCP server, tool name, and arguments to invoke from an MCP App view. +/// The currently selected custom agent, or null when using the default agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsCallToolRequest +public sealed class AgentGetCurrentResult { - /// 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; + /// 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 SessionAgentGetCurrentRequest +{ /// 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. +/// The newly selected custom agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsSetHostContextDetails +public sealed class AgentSelectResult { - /// 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; } + /// The newly selected custom agent. + [JsonPropertyName("agent")] + public AgentInfo Agent { get => field ??= new(); set; } } -/// Host context to advertise to MCP App guests. +/// Name of the custom agent to select for subsequent turns. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsSetHostContextRequest +internal sealed class AgentSelectRequest { - /// Host context advertised to MCP App guests. - [JsonPropertyName("context")] - public McpAppsSetHostContextDetails Context { get => field ??= new(); set; } + /// Name of the custom agent to select. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Current host context. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsHostContextDetails +internal sealed class SessionAgentDeselectRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Current host context advertised to MCP App guests. +/// Custom agents available to the session after reloading definitions from disk. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsHostContext +public sealed class AgentReloadResult { - /// Current host context. - [JsonPropertyName("context")] - public McpAppsHostContextDetails Context { get => field ??= new(); set; } + /// Reloaded custom agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpAppsGetHostContextRequest +internal sealed class SessionAgentReloadRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Capability negotiation snapshot. +/// Identifier assigned to the newly started background agent task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsDiagnoseCapability +public sealed class TasksStartAgentResult { - /// 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; } + /// Generated agent ID for the background task. + [JsonPropertyName("agentId")] + public string AgentId { get; set; } = string.Empty; } -/// What the server returned for this session. +/// Agent type, prompt, name, and optional description and model override for the new task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsDiagnoseServer +internal sealed class TasksStartAgentRequest { - /// 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; } + /// Type of agent to start (e.g., 'explore', 'task', 'general-purpose'). + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; - /// Tools whose `_meta.ui` is populated (resourceUri and/or visibility set). - [JsonPropertyName("toolsWithUiMeta")] - public double ToolsWithUiMeta { get; set; } -} + /// Short description of the task. + [JsonPropertyName("description")] + public string? Description { 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; } + /// Optional model override. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// What the server returned for this session. - [JsonPropertyName("server")] - public McpAppsDiagnoseServer Server { get => field ??= new(); set; } -} + /// Friendly, non-unique name used when displaying the agent. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; -/// 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; + /// 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; } -/// MCP resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +/// 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 McpResourceContent +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(TaskInfoAgent), "agent")] +[JsonDerivedType(typeof(TaskInfoClient), "client")] +[JsonDerivedType(typeof(TaskInfoShell), "shell")] +public partial class TaskInfo { - /// 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; + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { 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. +/// 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 McpResourcesReadRequest +public partial class TaskInfoAgent : TaskInfo { - /// 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; + /// + [JsonIgnore] + public override string Type => "agent"; - /// Resource URI. - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; -} + /// ISO 8601 timestamp when the current active period began. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } -/// 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; } + /// Accumulated active execution time in milliseconds. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeTimeMs")] + public TimeSpan? ActiveTime { get; set; } - /// Intended audience roles for this resource. - [JsonPropertyName("audience")] - public IList? Audience { get; set; } + /// Type of agent running this task. + [JsonPropertyName("agentType")] + public required string AgentType { get; set; } - /// Last-modified timestamp hint. - [JsonPropertyName("lastModified")] - public string? LastModified { 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; } - /// Priority hint for model/client use. - [JsonPropertyName("priority")] - public double? Priority { get; set; } -} + /// ISO 8601 timestamp when the task finished. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { 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; } + /// Short description of the task. + [JsonPropertyName("description")] + public required string Description { get; set; } - /// Icon MIME type, when known. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Icon sizes hint. - [JsonPropertyName("sizes")] - public string? Sizes { get; set; } + /// Friendly, non-unique name intended for display. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// Icon URI. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; + /// Error message when the task failed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Theme hint for this icon. - [JsonPropertyName("theme")] - public string? Theme { get; set; } -} + /// Whether task execution is synchronously awaited or managed in the background. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executionMode")] + public TaskExecutionMode? ExecutionMode { 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; } + /// Unique task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } - /// Server-provided non-standard descriptor fields preserved from the MCP response. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// ISO 8601 timestamp when the agent entered idle state. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } - /// Model/client annotations associated with this resource. - [JsonPropertyName("annotations")] - public McpResourceAnnotations? Annotations { get; set; } + /// Most recent response text from the agent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("latestResponse")] + public string? LatestResponse { get; set; } - /// Optional description of what this resource represents. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Requested model override for the task when specified. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Icons associated with this resource. - [JsonPropertyName("icons")] - public IList? Icons { 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; } - /// MIME type of the resource, if known. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } + /// Runtime model resolved for the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { get; set; } - /// The programmatic name of the resource. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Result text from the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public string? Result { get; set; } - /// Resource size in bytes, when known. - [JsonPropertyName("size")] - public long? Size { get; set; } + /// ISO 8601 timestamp when the task was started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Current lifecycle status of the task. + [JsonPropertyName("status")] + public required TaskStatus Status { get; set; } - /// The resource URI (e.g. ui://... or file:///...). - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// Tool call ID associated with this agent task. + [JsonPropertyName("toolCallId")] + public required string ToolCallId { get; set; } } -/// One page of resources advertised by the named MCP server. +/// Public owner attribution for a client-owned task. Identifiers are opaque and never authorize requests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesListResult +public sealed class TaskClientOwner { - /// Opaque cursor for the next page, if the server has more resources. - [JsonPropertyName("nextCursor")] - public string? NextCursor { get; set; } + /// ISO 8601 timestamp when the bound join disconnected. + [JsonPropertyName("disconnectedAt")] + public DateTimeOffset? DisconnectedAt { get; set; } - /// Resources advertised by the server (proxied MCP `resources/list`). - [JsonPropertyName("resources")] - public IList Resources { get => field ??= []; set; } -} + /// Display-only owner name. + [JsonPropertyName("displayName")] + public string? DisplayName { get; 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; } + /// Opaque identity of the currently or most recently bound session join. + [JsonPropertyName("joinId")] + public string JoinId { get; set; } = string.Empty; - /// 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; + /// Class of the task owner. + [JsonPropertyName("kind")] + public TaskClientOwnerKind Kind { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// 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`. +/// Tracked client-owned task metadata. +/// The client variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceTemplate +public partial class TaskInfoClient : TaskInfo { - /// Resource-template-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } + /// + [JsonIgnore] + public override string Type => "client"; - /// Server-provided non-standard descriptor fields preserved from the MCP response. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// ISO 8601 timestamp when the current active segment started. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } - /// Model/client annotations associated with this template. - [JsonPropertyName("annotations")] - public McpResourceAnnotations? Annotations { get; set; } + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeTimeMs")] + public required long ActiveTimeMs { get; set; } - /// Optional description of what this template is for. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Whether the currently bound owner can receive a cancellation request. + [JsonPropertyName("canCancel")] + public required bool CanCancel { get; set; } - /// Icons associated with resources matching this template. - [JsonPropertyName("icons")] - public IList? Icons { get; set; } + /// Human-readable reason for terminal cancellation. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cancellationReason")] + public string? CancellationReason { get; set; } - /// MIME type for resources matching this template, if uniform. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } + /// Owner-scoped registration and reclaim key. + [JsonPropertyName("clientTaskId")] + public required string ClientTaskId { get; set; } - /// The programmatic name of the resource template. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// ISO 8601 timestamp when the task reached a terminal status. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Task description. + [JsonPropertyName("description")] + public required string Description { get; set; } - /// An RFC 6570 URI template for constructing resource URIs. - [JsonPropertyName("uriTemplate")] - public string UriTemplate { get; set; } = string.Empty; -} + /// Optional task display name. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } -/// 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; } + /// Human-readable terminal failure message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Resource templates advertised by the server (proxied MCP `resources/templates/list`). - [JsonPropertyName("resourceTemplates")] - public IList ResourceTemplates { get => field ??= []; set; } -} + /// Optional owner-supplied terminal failure code. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; 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; } + /// Execution mode, which is always background for client-owned tasks. + [JsonPropertyName("executionMode")] + public required TaskClientExecutionMode ExecutionMode { 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; + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } - /// 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; } -/// 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; } + /// ISO 8601 timestamp of the most recent orphan transition. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("orphanedAt")] + public DateTimeOffset? OrphanedAt { get; set; } - /// Server-provided non-standard argument fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// Public attribution and presence for the task owner. + [JsonPropertyName("owner")] + public required TaskClientOwner Owner { get; set; } - /// Description of the argument. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// ISO 8601 timestamp of the most recent successful reclaim. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reclaimedAt")] + public DateTimeOffset? ReclaimedAt { get; set; } - /// Name of the argument. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Opaque successful terminal result supplied by the task owner. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } - /// Whether the argument is required; omission is distinct from false. - [JsonPropertyName("required")] - public bool? Required { 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; } } -/// An MCP prompt icon with standard size hints and preserved non-standard fields. +/// 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)] -public sealed class McpPromptIcon +public partial class TaskInfoShell : TaskInfo { - /// Server-provided non-standard icon fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// + [JsonIgnore] + public override string Type => "shell"; - /// Icon MIME type, when known. - [JsonPropertyName("mimeType")] - public string? MimeType { 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; } - /// Icon sizes, such as `48x48` or `any`. - [JsonPropertyName("sizes")] - public IList? Sizes { get; set; } + /// Whether this shell task can be promoted to background mode. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("canPromoteToBackground")] + public bool? CanPromoteToBackground { get; set; } - /// Icon URI. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; + /// Command being executed. + [JsonPropertyName("command")] + public required string Command { get; set; } - /// Theme hint for this icon. - [JsonPropertyName("theme")] - public string? Theme { get; set; } -} + /// ISO 8601 timestamp when the task finished. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { 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; } + /// Short description of the task. + [JsonPropertyName("description")] + public required string Description { get; set; } - /// Server-provided non-standard descriptor fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// Whether task execution is synchronously awaited or managed in the background. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executionMode")] + public TaskExecutionMode? ExecutionMode { get; set; } - /// Arguments accepted by the prompt. - [JsonPropertyName("arguments")] - public IList? Arguments { get; set; } + /// Unique task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } - /// Description of what this prompt provides. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Path to the detached shell log, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logPath")] + public string? LogPath { get; set; } - /// Icons associated with this prompt. - [JsonPropertyName("icons")] - public IList? Icons { get; set; } + /// Process ID when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } - /// The programmatic name of the prompt. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// ISO 8601 timestamp when the task was started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } - /// Human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Current lifecycle status of the task. + [JsonPropertyName("status")] + public required TaskStatus Status { get; set; } } -/// One page of prompts advertised by the named MCP server. +/// Background tasks currently tracked by the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptsListResult +public sealed class TaskList { - /// 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; } + /// Currently tracked tasks. + [JsonPropertyName("tasks")] + public IList Tasks { get => field ??= []; set; } } -/// MCP server whose prompts to enumerate. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpPromptsListRequest +internal sealed class SessionTasksListRequest { - /// 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. +/// Tracked client-owned task metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpPromptMessage +public sealed class TaskClientInfo { - /// Message-level metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } + /// ISO 8601 timestamp when the current active segment started. + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } - /// Server-provided non-standard message fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeTimeMs")] + public long ActiveTimeMs { get; set; } - /// The original MCP content block, including nested metadata and unfamiliar content types. - [JsonPropertyName("content")] - public JsonElement Content { get; set; } + /// Whether the currently bound owner can receive a cancellation request. + [JsonPropertyName("canCancel")] + public bool CanCancel { get; set; } - /// The role of the message sender. - [JsonPropertyName("role")] - public McpPromptRole Role { get; set; } -} + /// Human-readable reason for terminal cancellation. + [JsonPropertyName("cancellationReason")] + public string? CancellationReason { 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; } + /// Owner-scoped registration and reclaim key. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { get; set; } = string.Empty; - /// Server-provided non-standard result fields. - [JsonPropertyName("additionalProperties")] - public IDictionary? AdditionalProperties { get; set; } + /// ISO 8601 timestamp when the task reached a terminal status. + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } - /// Description of the prompt. + /// Task description. [JsonPropertyName("description")] - public string? Description { get; set; } + public string Description { get; set; } = string.Empty; - /// Ordered prompt messages. - [JsonPropertyName("messages")] - public IList Messages { get => field ??= []; set; } -} + /// Optional task display name. + [JsonPropertyName("displayName")] + public string? DisplayName { get; 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; } + /// Human-readable terminal failure message. + [JsonPropertyName("error")] + public string? Error { 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; + /// Optional owner-supplied terminal failure code. + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } - /// 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; + /// Execution mode, which is always background for client-owned tasks. + [JsonPropertyName("executionMode")] + public TaskClientExecutionMode ExecutionMode { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { 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; } -} + /// ISO 8601 timestamp when the connected owner entered idle status. + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { 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; } + /// 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; } } -/// Per-source session diagnostics configuration. +/// Result of registering or reclaiming a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiagnosticsConfiguration +public sealed class TasksRegisterResult { - /// Diagnostic thresholds keyed by supported source. - [JsonPropertyName("sources")] - public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } + /// 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; } } -/// Patch session diagnostic thresholds for explicitly supplied sources. +/// Registers or reclaims a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiagnosticsConfigureRequest +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; - /// Sources to configure. At least one supported source must be supplied. - [JsonPropertyName("sources")] - public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } + /// Task kind. + [JsonPropertyName("type")] + public TaskClientType Type { 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. +/// Result of publishing a client-owned task update. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class TasksUpdateResult +{ + /// 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; } +} + +/// Progress or terminal update for a client-owned task. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "source", + TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(DiagnosticEntryMcp), "mcp")] -public partial class DiagnosticEntry +[JsonDerivedType(typeof(TaskClientUpdateProgress), "progress")] +[JsonDerivedType(typeof(TaskClientUpdateCompleted), "completed")] +[JsonDerivedType(typeof(TaskClientUpdateFailed), "failed")] +[JsonDerivedType(typeof(TaskClientUpdateCancelled), "cancelled")] +public partial class TaskClientUpdate { /// The type discriminator. - [JsonPropertyName("source")] - public virtual string Source { get; set; } = string.Empty; + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// MCP-specific detail for a source-discriminated diagnostic entry. +/// Publishes nonterminal progress for a running or idle client task. +/// The progress variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDiagnosticDetails +public partial class TaskClientUpdateProgress : TaskClientUpdate { - /// Fresh identifier for the MCP connection attempt, including failed starts. - [JsonPropertyName("connectionId")] - public string ConnectionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "progress"; - /// Serialized diagnostic detail. Protocol and HTTP records use JSON when detail is present. - [JsonPropertyName("data")] - public string? Data { get; set; } + /// Optional progress message appended to recent activity when nonempty. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Protocol-frame direction when kind is protocol. - [JsonPropertyName("direction")] - public McpDiagnosticDirection? Direction { get; set; } + /// Optional completion percentage; null clears the current percentage. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("percentage")] + public double? Percentage { get; set; } - /// Diagnostic record category. - [JsonPropertyName("kind")] - public McpDiagnosticKind Kind { get; set; } + /// Optional progress phase; null clears the current phase. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("phase")] + public string? Phase { get; set; } - /// Configured MCP server name. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Optional active status transition. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("status")] + public TaskClientActiveStatus? Status { get; set; } } -/// The mcp variant of . +/// Reports successful terminal completion. +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DiagnosticEntryMcp : DiagnosticEntry +public partial class TaskClientUpdateCompleted : TaskClientUpdate { /// [JsonIgnore] - public override string Source => "mcp"; + public override string Kind => "completed"; - /// Agent identifier for a subagent host. Omitted for the root agent. + /// Optional final progress message. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("agentId")] - public string? AgentId { get; set; } - - /// Typed MCP diagnostic detail. - [JsonPropertyName("details")] - public required McpDiagnosticDetails Details { get; set; } + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Severity of this emitted diagnostic record. - [JsonPropertyName("level")] - public required DiagnosticSeverity Level { get; set; } + /// Optional opaque successful terminal result. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } +} - /// Human-readable diagnostic summary. - [JsonPropertyName("message")] - public required string Message { 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"; - /// Original byte count when a known-size message or data value was truncated. + /// Optional owner-supplied terminal failure code. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("originalBytes")] - public long? OriginalBytes { get; set; } + [JsonPropertyName("code")] + public string? Code { get; set; } - /// UTC RFC 3339 timestamp captured at the diagnostic source. - [JsonPropertyName("timestamp")] - public required string Timestamp { get; set; } + /// Human-readable terminal failure message. + [JsonPropertyName("error")] + public required string Error { get; set; } - /// Whether message or data was truncated to the record-size bound. + /// Optional final progress message. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("truncated")] - public bool? Truncated { get; set; } + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// One cursor-addressed page of retained session diagnostics. +/// Reports terminal cancellation after external work stopped. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiagnosticsReadResult +public partial class TaskClientUpdateCancelled : TaskClientUpdate { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Retained records beginning at the requested cursor. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Whether additional retained records follow this page. - [JsonPropertyName("hasMore")] - public bool HasMore { get; set; } + /// Optional human-readable cancellation reason. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } } -/// Cursor-based request for session diagnostics. The default limit is 100 (maximum 500); the default waitMs is zero (maximum 30000). +/// Updates a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiagnosticsReadRequest +internal sealed class TasksUpdateRequest { - /// Opaque cursor returned by an earlier read. Omit to start at the oldest retained record. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Maximum number of records to return, from 1 through 500. Omit for 100. - [JsonPropertyName("max")] - public long? Max { get; set; } + /// Owner update sequence to apply. + [JsonPropertyName("sequence")] + public long Sequence { 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; } + /// Progress or terminal update payload. + [JsonPropertyName("update")] + public TaskClientUpdate Update { get => field ??= new(); set; } } -/// Feature detection and hard polling limits for the EXPERIMENTAL session connector API. +/// 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 ConnectorCapabilities +public sealed class TasksRefreshResult { - /// 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 +internal sealed class SessionTasksRefreshRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Credential-free identity metadata. +/// 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)] -public sealed class AuthIdentityMetadata +public sealed class TasksWaitForPendingResult { - /// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorSessionAccount +internal sealed class SessionTasksWaitForPendingRequest { - /// Opaque session-scoped account selection ID. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Credential-free identity metadata. - [JsonPropertyName("authInfo")] - public AuthIdentityMetadata AuthInfo { get => field ??= new(); set; } +/// 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 = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(TaskProgressAgent), "agent")] +[JsonDerivedType(typeof(TaskProgressClient), "client")] +[JsonDerivedType(typeof(TaskProgressShell), "shell")] +public partial class TaskProgress +{ + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// Identifies the target session. + +/// Timestamped display line for task progress output or recent agent activity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetAccountRequest +public sealed class TaskProgressLine { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Display message, e.g., "▸ bash", "✓ edit src/foo.ts". + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; + + /// ISO 8601 timestamp when this event occurred. + [JsonPropertyName("timestamp")] + public DateTimeOffset Timestamp { get; set; } } -/// Account-targeted authorization update required by the Connector service. The account ID is an opaque host routing identifier; no credential is included. +/// 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 sealed class ConnectorAuthorizationRequirement +public partial class TaskProgressAgent : TaskProgress { - /// Exact opaque account selection that made the Connector request. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Type => "agent"; - /// Stable OAuth scope the selected account must grant. - [JsonPropertyName("scope")] - public ConnectorAuthorizationScope Scope { 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; } } -/// Credential-free Connector catalog entry. +/// Generic progress for a client-owned task. +/// The client variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorCatalogEntry +public partial class TaskProgressClient : TaskProgress { - /// Untrusted service description, when present. - [JsonPropertyName("description")] - public string? Description { get; set; } + /// + [JsonIgnore] + public override string Type => "client"; - /// Untrusted display label from the service. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Most recent nonempty progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("lastMessage")] + public string? LastMessage { get; set; } - /// Optional catalog logo. - [JsonPropertyName("logo")] - public string? Logo { get; set; } + /// Current completion percentage from zero through one hundred. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("percentage")] + public double? Percentage { get; set; } - /// Canonical Connector name used by lifecycle methods. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Current owner-defined progress phase. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("phase")] + public string? Phase { get; set; } - /// Optional catalog release tag. - [JsonPropertyName("releaseTag")] - public string? ReleaseTag { get; set; } + /// Recent server-timestamped progress messages. + [JsonPropertyName("recentActivity")] + public required IList RecentActivity { get; set; } - /// Opaque stable runtime IDs currently projected into the session for this Connector. - [JsonPropertyName("runtimeServerIds")] - public IList RuntimeServerIds { get => field ??= []; set; } + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public required long Sequence { get; set; } - /// Current authoritative service connection state. + /// Current client task lifecycle status. [JsonPropertyName("status")] - public ConnectorCatalogStatus Status { get; set; } + public required TaskClientStatus Status { get; set; } - /// Optional catalog tier. - [JsonPropertyName("tier")] - public string? Tier { get; set; } + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public required DateTimeOffset UpdatedAt { get; set; } } -/// Validated Connector catalog snapshot cached by the session. +/// 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 sealed class ConnectorCatalogResult +public partial class TaskProgressShell : TaskProgress { - /// Validated catalog entries in service order. - [JsonPropertyName("connectors")] - public IList Connectors { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Type => "shell"; - /// Unix epoch milliseconds when this snapshot was accepted. - [JsonPropertyName("refreshedAtMs")] - public long RefreshedAtMs { get; set; } + /// Process ID when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } - /// Monotonically increasing session-local catalog revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } + /// Recent stdout/stderr lines from the running shell command. + [JsonPropertyName("recentOutput")] + public required string RecentOutput { get; set; } } -/// Live status of one session-owned MCP projection. +/// Progress information for the task, or null when no task with that ID is tracked. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorRuntimeStatus +public sealed class TasksGetProgressResult { - /// 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; } + /// 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; } } -/// Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. +/// Identifier of the background task to fetch progress for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorStatus +internal sealed class TasksGetProgressRequest { - /// 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; } + /// Task identifier (agent ID or shell ID). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Number of active opaque connection continuations. - [JsonPropertyName("pendingConnections")] - public long PendingConnections { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Live MCP status for every Connector-owned runtime server. - [JsonPropertyName("runtimeServers")] - public IList RuntimeServers { get => field ??= []; set; } +/// The first sync-waiting task that can currently be promoted to background mode. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class TasksGetCurrentPromotableResult +{ + /// 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; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetStatusRequest +internal sealed class SessionTasksGetCurrentPromotableRequest { /// 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. +/// Indicates whether the task was successfully promoted to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorAccountRequest +public sealed class TasksPromoteToBackgroundResult { - /// Opaque account selection ID previously returned by an account discovery API. - [JsonPropertyName("accountId")] - public string AccountId { get; set; } = string.Empty; + /// Whether the task was successfully promoted to background mode. + [JsonPropertyName("promoted")] + public bool Promoted { get; set; } +} + +/// Identifier of the task to promote to background mode. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class TasksPromoteToBackgroundRequest +{ + /// Task identifier. + [JsonPropertyName("id")] + public string Id { 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. +/// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. [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 +public sealed class TasksPromoteCurrentToBackgroundResult { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// 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; } } - -/// The service is connected and the session MCP graph was reconciled. -/// The connected variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultConnected : ConnectorConnectResult +internal sealed class SessionTasksPromoteCurrentToBackgroundRequest { - /// - [JsonIgnore] - public override string Kind => "connected"; - - /// Fresh authoritative Connector state after MCP reconciliation. - [JsonPropertyName("status")] - public required ConnectorStatus Status { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Host-owned consent is required before bounded continuation can complete. -/// The consent_required variant of . +/// Indicates whether the background task was successfully cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultConsentRequired : ConnectorConnectResult +public sealed class TasksCancelResult { - /// - [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; } + /// Whether the task was successfully cancelled. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// The service is still completing the connection without a consent URL. -/// The pending variant of . +/// Identifier of the background task to cancel. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultPending : ConnectorConnectResult +internal sealed class TasksCancelRequest { - /// - [JsonIgnore] - public override string Kind => "pending"; + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Opaque ID accepted by continueConnection. - [JsonPropertyName("continuationId")] - public required string ContinuationId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Selects one Connector and the pinned host-owned account used for its service and MCP authorization. +/// Indicates whether the task was removed. False when the task does not exist or is still running/idle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorConnectRequest +public sealed class TasksRemoveResult { - /// 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; + /// 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; } +} - /// Canonical Connector name from the current catalog. - [JsonPropertyName("connectorName")] - public string ConnectorName { get; set; } = string.Empty; +/// Identifier of the completed or cancelled task to remove from tracking. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class TasksRemoveRequest +{ + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Explicitly bounded continuation of a pending Connector connection. +/// Indicates whether the message was delivered, with an error message when delivery failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorContinueRequest +public sealed class TasksSendMessageResult { - /// Opaque continuation ID returned by connect, reconnect, or an earlier continuation. - [JsonPropertyName("continuationId")] - public string ContinuationId { get; set; } = string.Empty; + /// Error message if delivery failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Maximum wall-clock duration in milliseconds for this call. Must be between one and the capability limit. - [JsonPropertyName("deadlineMs")] - public int DeadlineMs { get; set; } + /// Whether the message was successfully delivered or steered. + [JsonPropertyName("sent")] + public bool Sent { get; set; } +} - /// Maximum catalog requests made by this call. Must be between one and the capability limit. - [JsonPropertyName("maxAttempts")] - public int MaxAttempts { get; set; } +/// Identifier of the target agent task, message content, and optional sender agent ID. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class TasksSendMessageRequest +{ + /// Agent ID of the sender, if sent on behalf of another agent. + [JsonPropertyName("fromAgentId")] + public string? FromAgentId { get; set; } - /// Delay in milliseconds between attempts. Must not exceed the capability limit. - [JsonPropertyName("pollIntervalMs")] - public int PollIntervalMs { 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; } -/// Authoritative result after disconnect and MCP reconciliation. +/// Skill metadata available to a session, with name, description, source, enabled/invocable state, path, plugin, and argument hint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorDisconnectResult +public sealed class Skill { - /// Whether the service accepted the idempotent disconnect. - [JsonPropertyName("disconnected")] - public bool Disconnected { get; set; } + /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. + [JsonPropertyName("argumentHint")] + public string? ArgumentHint { get; set; } - /// Fresh authoritative session state after removing Connector-owned MCP servers. - [JsonPropertyName("status")] - public ConnectorStatus Status { get => field ??= new(); set; } -} + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; 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; } + /// Description of what the skill does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. - [JsonPropertyName("forceConnectorName")] - public string? ForceConnectorName { get; set; } + /// Whether the skill is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. - [JsonPropertyName("refreshCatalog")] - public bool? RefreshCatalog { get; set; } -} + /// Unique identifier for the skill. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; -/// 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; + /// Absolute path to the skill file. + [JsonPropertyName("path")] + public string? Path { get; set; } - /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. - [JsonPropertyName("forceConnectorName")] - public string? ForceConnectorName { get; set; } + /// Name of the plugin that provides the skill, when source is 'plugin'. + [JsonPropertyName("pluginName")] + public string? PluginName { 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 location type (e.g., project, personal-copilot, plugin, builtin). + [JsonPropertyName("source")] + public SkillSource Source { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the skill can be invoked by the user as a slash command. + [JsonPropertyName("userInvocable")] + public bool UserInvocable { get; set; } } -/// Identifies the target session. +/// Skills available to the session, with their enabled state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsWithdrawProjectionRequest +public sealed class SkillList { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Available skills. + [JsonPropertyName("skills")] + public IList Skills { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionManagedSettingsGetRequest +internal sealed class SessionSkillsListRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Session plugin metadata, with name, marketplace, optional version, and enabled state. +/// Skill invocation record with name, path, content, allowed tools, and turn number. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Plugin +public sealed class SkillsInvokedSkill { - /// 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; } + /// Tools that should be auto-approved when this skill is active, captured at invocation time. + [JsonPropertyName("allowedTools")] + public IList? AllowedTools { get; set; } - /// Whether enterprise managed settings control this plugin. - [JsonPropertyName("managed")] - public bool? Managed { get; set; } + /// Full content of the skill file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Enabled state required by enterprise managed settings. - [JsonPropertyName("managedDesiredEnabled")] - public bool? ManagedDesiredEnabled { get; set; } + /// Whether model invocation was disabled when this skill was invoked. + [JsonPropertyName("disableModelInvocation")] + public bool? DisableModelInvocation { get; set; } - /// Marketplace the plugin came from. - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; + /// Turn number when the skill was invoked. + [JsonPropertyName("invokedAtTurn")] + public long InvokedAtTurn { get; set; } - /// Plugin name. + /// Unique identifier for the skill. [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; } + /// 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; } -/// Plugins installed for the session, with their enabled state and version metadata. +/// Skills invoked during this session, ordered by invocation time (most recent last). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginList +public sealed class SkillsGetInvokedResult { - /// Installed plugins. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; 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 SessionPluginsListRequest +internal sealed class SessionSkillsGetInvokedRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Plugin source resolved relative to the session's authoritative working directory. +/// Name of the skill to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsInstallRequest +internal sealed class SkillsEnableRequest { + /// Name of the skill to enable. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + /// 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. +/// Name of the skill to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUninstallRequestWithSession +internal sealed class SkillsDisableRequest { - /// 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. + /// Name of the skill to disable. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; @@ -19432,8770 +18883,12694 @@ internal sealed class PluginsUninstallRequestWithSession public string SessionId { get; set; } = string.Empty; } -/// Name (or spec) of the plugin to update. +/// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUpdateRequestWithSession +public sealed class SkillsLoadDiagnostics { - /// Plugin name or "plugin@marketplace" spec to update. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Errors emitted while loading skills (e.g. skills that failed to load entirely). + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Warnings emitted while loading skills (e.g. skills that loaded but had issues). + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Plugin names (or specs) to enable in the session's authoritative working directory. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsEnableRequest +internal sealed class SessionSkillsReloadRequest { - /// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsDisableRequest +internal sealed class SessionSkillsEnsureLoadedRequest { - /// 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. +/// The IDE a host is connected to, as reported to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsReloadRequest +public sealed class SessionConnectedIdeInfo { - /// 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; } + /// Display name of the connected IDE, for example `VS Code`. + [JsonPropertyName("ideName")] + public string IdeName { get; set; } = string.Empty; - /// Reload MCP server connections after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadMcp")] - public bool? ReloadMcp { get; set; } + /// Absolute path of the workspace folder the IDE has open. + [JsonPropertyName("workspaceFolder")] + public string WorkspaceFolder { get; set; } = string.Empty; } -/// RPC data type for SessionPluginsReloadRequestWithSession operations. +/// Records which IDE the host is connected to, or clears it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsReloadRequestWithSession +internal sealed class SessionMcpSetConnectedIdeInfoParams { - /// 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; } + /// 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; } -/// Retry outcome for one plugin required by managed settings. +/// Recorded MCP server connection failure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ManagedPluginRetryEntry +public sealed class McpServerFailureInfo { - /// 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; + /// Failure message produced when the MCP server connection failed. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// What the retry did for this plugin. - [JsonPropertyName("status")] - public ManagedPluginRetryStatus Status { get; set; } + /// epoch-ms timestamp at which the failure was recorded. + [JsonPropertyName("timestamp")] + public long Timestamp { get; set; } } -/// Per-plugin outcomes of retrying the plugins required by the session's managed settings. +/// Recorded MCP server pending-auth state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsRetryManagedResult +public sealed class McpServerNeedsAuthInfo { - /// 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; } + /// epoch-ms timestamp at which the server signalled it needs authentication. + [JsonPropertyName("timestamp")] + public long Timestamp { get; set; } } -/// RPC data type for SessionPluginsRetryManaged operations. +/// Host-level state, omitted when no MCP host is initialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsRetryManagedRequest +public sealed class McpHostState { - /// "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. - [JsonPropertyName("plugins")] - public IList? Plugins { get; set; } -} + /// Names of currently-connected MCP clients. + [JsonPropertyName("clients")] + public IList Clients { get => field ??= []; 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; } + /// Configured servers that are explicitly disabled. + [JsonPropertyName("disabledServers")] + public IList DisabledServers { get => field ??= []; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Map of server name to recorded connection failure. + [JsonPropertyName("failedServers")] + public IDictionary FailedServers { get => field ??= new Dictionary(); set; } -/// 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; -} + /// Configured servers filtered out by MCP server policy. + [JsonPropertyName("filteredServers")] + public IList FilteredServers { get => field ??= []; set; } -/// 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; + /// Whether third-party MCP servers are policy-enabled for this session. + [JsonPropertyName("mcp3pEnabled")] + public bool Mcp3pEnabled { 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; + /// Map of server name to recorded pending-auth state. + [JsonPropertyName("needsAuthServers")] + public IDictionary NeedsAuthServers { get => field ??= new Dictionary(); 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; } + /// Names of servers with in-flight connection attempts. + [JsonPropertyName("pendingConnections")] + public IList PendingConnections { get => field ??= []; set; } } -/// Name of the marketplace to remove and an optional force flag. +/// Owned installation that a listed MCP server's live configuration came from. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesRemoveRequestWithSession +public sealed class McpServerOwnership { - /// 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; } + /// Stable installation identifier from the owned installation receipt. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; +} - /// Marketplace name to remove. +/// 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; } + + /// 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; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } + + /// 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; } } -/// Name of the marketplace whose plugin catalog to fetch. +/// MCP servers configured for the session, with their connection status and host-level state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesBrowseRequestWithSession +public sealed class McpServerList { - /// Marketplace name to browse. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpListRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// RPC data type for SessionPluginsMarketplacesRefresh operations. +/// Observational state for a matching already materialized MCP server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsMarketplacesRefreshRequest +public sealed class McpConfiguredServerState { - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// 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; } } -/// RPC data type for SessionPluginsMarketplacesRefreshRequestWithSession operations. +/// Effective MCP configuration entry. Configuration enablement is distinct from the optional live observation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsMarketplacesRefreshRequestWithSession +public sealed class McpConfiguredServer { - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. + /// 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; } + public string Name { get; set; } = string.Empty; + + /// Configuration provenance: 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; } +} + +/// Effective MCP configuration with optional live observations from matching already materialized servers. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpConfiguredServerList +{ + /// Effective configured MCP servers. + [JsonPropertyName("servers")] + public IList Servers { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpListConfiguredRequest +{ /// 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. +/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderSessionToken +public sealed class McpToolUi { - /// 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; } + /// 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; } - /// The short-lived token value. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + [JsonPropertyName("visibility")] + public IList? Visibility { get; set; } } -/// A snapshot of the provider endpoint the session is currently configured to talk to. +/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderEndpoint +public sealed class McpTools { - /// 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; } + /// Tool description, when provided. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Provider family. Matches the `type` field of a BYOK provider config. - [JsonPropertyName("type")] - public ProviderEndpointType Type { get; set; } + /// Tool name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Wire API to be used, when required for the provider type. - [JsonPropertyName("wireApi")] - public ProviderEndpointWireApi? WireApi { 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; } } -/// RPC data type for SessionProviderGetEndpoint operations. +/// Tools exposed by the connected MCP server. Throws when the server is not connected. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionProviderGetEndpointRequest +public sealed class McpListToolsResult { - /// 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; } + /// Tools exposed by the server. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } } -/// RPC data type for SessionProviderGetEndpointRequestWithSession operations. +/// Server name whose tool list should be returned. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionProviderGetEndpointRequestWithSession +internal sealed class McpListToolsRequest { - /// 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; } + /// 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; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The selectable model entries synthesized for the models added by this call. +/// Name of the MCP server to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderAddResult +public sealed class McpEnableRequest { - /// 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; } + /// 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; } } -/// BYOK providers and/or models to add to the session's registry at runtime. Both fields are optional; provide providers, models, or both. +/// Name of the MCP server to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderAddRequest +internal sealed class McpEnableRequestWithSession { - /// 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; } + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { 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; } + /// 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; } -/// The selectable model entries and selection ids synthesized for the synchronized BYOK models. +/// Name of the MCP server to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderSyncResult +public sealed class McpDisableRequest { - /// 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; } + /// Required for an owned installation; omission preserves only manual-server behaviour. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Provider-qualified model selection ids present after synchronization. - [JsonPropertyName("selectionIds")] - public IList SelectionIds { get => field ??= []; 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; } } -/// Authoritative BYOK provider and model registry snapshot to apply atomically to the session. +/// Name of the MCP server to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderSyncRequest +internal sealed class McpDisableRequestWithSession { - /// BYOK model definition snapshot. Models absent from this list are removed. - [JsonPropertyName("models")] - public IList? Models { get; set; } + /// Required for an owned installation; omission preserves only manual-server behaviour. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Named BYOK provider connection snapshot. Providers absent from this list are removed. - [JsonPropertyName("providers")] - public IList? Providers { 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; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// What the withdrawal actually removed from the registry. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderWithdrawResult +internal sealed class SessionMcpReloadRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Host-managed model selection ids to withdraw from the session's BYOK registry. +/// Result of moving in-flight MCP loading to the background. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderWithdrawRequest +public sealed class MoveMcpLoadingToBackgroundResult { - /// 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; } + /// 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; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpMoveLoadingToBackgroundRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the session options patch was applied successfully. +/// MCP server allowed by policy, with server name and optional PII-free explanatory note. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionUpdateOptionsResult +public sealed class McpAllowedServer { - /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated. - [JsonPropertyName("pluginHookCount")] - public long? PluginHookCount { get; set; } + /// Allowed server name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// PII-free note explaining why the server was allowed. + [JsonPropertyName("redactedNote")] + public string? RedactedNote { get; set; } } -/// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. +/// MCP server whose connection attempt failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicyRuleSource +public sealed class McpFailedServer { - /// Name of the policy source. + /// 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; - - /// 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. +/// MCP server filtered by policy, with name, reason, and optional redacted reason. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicyRule +public sealed class McpFilteredServer { - /// Conditions of which at least one must match. - [JsonPropertyName("ifAnyMatch")] - public IList? IfAnyMatch { 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; } - /// Conditions none of which may match. - [JsonPropertyName("ifNoneMatch")] - public IList? IfNoneMatch { get; set; } + /// Filtered server name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Path patterns covered by this rule. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } + /// Human-readable filter reason. + [JsonPropertyName("reason")] + public string Reason { 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; } + /// PII-free filter reason. + [JsonPropertyName("redactedReason")] + public string? RedactedReason { get; set; } } -/// Content-exclusion policy supplied to `session.options.update`, with rules, last-updated data, and scope. +/// MCP server startup filtering result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicy +internal sealed class McpStartServersResult { - /// Opaque policy update timestamp supplied by the host. - [JsonPropertyName("last_updated_at")] - public JsonElement LastUpdatedAt { get; set; } + /// Non-default servers allowed by policy. + [JsonPropertyName("allowedServers")] + public IList? AllowedServers { get; set; } - /// Content-exclusion rules to apply. - [JsonPropertyName("rules")] - public IList Rules { get => field ??= []; set; } + /// Servers whose connection attempt failed. + [JsonPropertyName("failedServers")] + public IList? FailedServers { get; set; } - /// Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. - [JsonPropertyName("scope")] - public OptionsUpdateAdditionalContentExclusionPolicyScope Scope { get; set; } + /// Servers filtered out before startup. + [JsonPropertyName("filteredServers")] + public IList FilteredServers { get => field ??= []; set; } } -/// Options scoped to the built-in CAPI (Copilot API) provider. +/// Opaque MCP reload configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CapiSessionOptions +internal sealed class McpReloadWithConfigRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Installed plugin record for a session, with marketplace, version, install time, enabled state, cache path, and source. +/// Outcome of an MCP sampling execution: success result, failure error, or cancellation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionInstalledPlugin +public sealed class McpSamplingExecutionResult { - /// 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; } + /// 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; } - /// Installation timestamp (ISO-8601). - [JsonPropertyName("installed_at")] - public string InstalledAt { get; set; } = string.Empty; + /// Error description, present when action='failure'. + [JsonPropertyName("error")] + public string? Error { 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; } + /// 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; } +} - /// Marketplace the plugin came from (empty string for direct repo installs). - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; +/// Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpExecuteSamplingParams +{ + /// 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; } - /// Plugin name. - [JsonPropertyName("name")] - public string Name { 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. + [JsonPropertyName("request")] + public JsonElement Request { get; set; } - /// Source descriptor for direct repo installs (when marketplace is empty). - [JsonPropertyName("source")] - public JsonElement? Source { 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; - /// 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; } + /// Name of the MCP server that initiated the sampling request. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// 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; } +} + +/// 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; +} + +/// Env-value mode recorded on the session after the update. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpSetEnvValueModeResult +{ + /// Mode recorded on the session after the update. + [JsonPropertyName("mode")] + public McpSetEnvValueModeDetails Mode { 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; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// 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; } +} + +/// 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; +} + +/// 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; } +} + +/// 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; +} + +/// 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; } + + /// 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; } +} + +/// 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; } + + /// 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; +} + +/// 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; } + + /// 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 required string ServerName { 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; } + + /// 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; +} + +/// Server name for an individual MCP server stop. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +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; +} + +/// Registration parameters for an external MCP client. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpRegisterExternalClientRequest +{ + /// Logical server name for the external client. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Server name identifying the external client to remove. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpUnregisterExternalClientRequest +{ + /// Server name of the external client to unregister. + [JsonPropertyName("serverName")] + public string ServerName { 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). +/// 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; } + + /// 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; } + + /// 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; } + + /// 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; } + + /// Stable integration identifier used for analytics and rate-limit attribution. + [JsonPropertyName("integrationId")] + public string? IntegrationId { get; set; } + + /// Whether experimental capabilities are enabled. + [JsonPropertyName("isExperimentalMode")] + public bool? IsExperimentalMode { get; set; } + + /// Whether interactive shell sessions are logged. + [JsonPropertyName("logInteractiveShells")] + public bool? LogInteractiveShells { get; set; } + + /// Identifier sent to LSP-style integrations. + [JsonPropertyName("lspClientName")] + public string? LspClientName { 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; } + + /// 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; } + + /// The model ID to use for assistant turns. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// Per-property model capability overrides for the selected model. + [JsonPropertyName("modelCapabilitiesOverrides")] + public ModelCapabilitiesOverride? ModelCapabilitiesOverrides { get; set; } + + /// Organization-level custom instructions to inject into the system prompt. + [JsonPropertyName("organizationCustomInstructions")] + public string? OrganizationCustomInstructions { get; set; } + + /// Custom model-provider configuration (BYOK). + [JsonPropertyName("provider")] + public ProviderConfig? Provider { 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; } + + /// Reasoning summary mode for supported model clients. + [JsonPropertyName("reasoningSummary")] + public OptionsUpdateReasoningSummary? ReasoningSummary { get; set; } + + /// Whether the session is running in an interactive UI. + [JsonPropertyName("runningInInteractiveMode")] + public bool? RunningInInteractiveMode { get; set; } + + /// Resolved sandbox configuration. + [JsonPropertyName("sandboxConfig")] + public SandboxConfig? SandboxConfig { 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; } + + /// 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; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Optional session limits. Pass null to clear the session limits. + [JsonPropertyName("sessionLimits")] + public SessionLimitsConfig? SessionLimits { get; set; } + + /// Per-session settings for built-in shell tools. + [JsonPropertyName("shell")] + public ShellOptions? Shell { 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; } + + /// PowerShell process flags applied to built-in and user-requested shell commands. + [JsonPropertyName("shellProcessFlags")] + public IList? ShellProcessFlags { get; set; } + + /// 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; } + + /// Whether to skip loading custom instruction sources. + [JsonPropertyName("skipCustomInstructions")] + public bool? SkipCustomInstructions { get; set; } + + /// Whether to skip embedding retrieval pipeline initialization and execution. + [JsonPropertyName("skipEmbeddingRetrieval")] + public bool? SkipEmbeddingRetrieval { 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; } + + /// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. + [JsonPropertyName("toolFilterPrecedence")] + public OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence { get; set; } + + /// Optional path for trajectory output. + [JsonPropertyName("trajectoryFile")] + public string? TrajectoryFile { 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; } +} + +/// 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; } + + /// 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; } +} + +/// 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; + + /// Extension name (directory name). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Process ID if the extension is running. + [JsonPropertyName("pid")] + public long? Pid { 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; } + + /// Current status: running, disabled, failed, or starting. + [JsonPropertyName("status")] + public ExtensionStatus Status { get; set; } +} + +/// Extensions discovered for the session, with their current status. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ExtensionList +{ + /// Discovered extensions and their current status. + [JsonPropertyName("extensions")] + public IList Extensions { get => field ??= []; 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; +} + +/// Source-qualified extension identifier to enable for the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ExtensionsEnableRequest +{ + /// 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; +} + +/// Source-qualified extension identifier to disable for the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +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; +} + +/// 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; +} + +/// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushAttachmentFileLineRange +{ + /// 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; } +} + +/// File attachment. +/// The file variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentFile : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "file"; + + /// 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; } +} + +/// Directory attachment. +/// The directory variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentDirectory : PushAttachment +{ + /// + [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; } +} + +/// End position of the selection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushAttachmentSelectionDetailsEnd +{ + /// 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; } +} + +/// Start position of the selection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushAttachmentSelectionDetailsStart +{ + /// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushAttachmentSelectionDetails +{ + /// 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; } +} + +/// Code selection attachment from an editor. +/// The selection variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentSelection : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "selection"; + + /// User-facing display name for the selection. + [JsonPropertyName("displayName")] + public required string DisplayName { get; set; } + + /// Absolute path to the file containing the selection. + [JsonPropertyName("filePath")] + public required string FilePath { get; set; } + + /// Position range of the selection within the file. + [JsonPropertyName("selection")] + public required PushAttachmentSelectionDetails Selection { get; set; } + + /// The selected text content. + [JsonPropertyName("text")] + public required string Text { get; set; } +} + +/// GitHub issue, pull request, or discussion reference. +/// The github_reference variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubReference : PushAttachment +{ + /// + [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; } +} + +/// Pointer to a GitHub repository. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushGitHubRepoRef +{ + /// 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; +} + +/// Pointer to a GitHub commit. +/// The github_commit variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubCommit : PushAttachment +{ + /// + [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 . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubRelease : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "github_release"; + + /// Human-readable release name. + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// Repository the release belongs to. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } + + /// Git tag the release is anchored to. + [JsonPropertyName("tagName")] + public required string TagName { get; set; } + + /// URL to the release on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } +} + +/// 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"; + + /// 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; } + + /// Job id within the workflow run. + [JsonPropertyName("jobId")] + public required long JobId { get; set; } + + /// Display name of the job. + [JsonPropertyName("jobName")] + public required string JobName { get; set; } + + /// Repository the workflow run belongs to. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } + + /// URL to the job on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } + + /// Display name of the workflow the job ran in. + [JsonPropertyName("workflowName")] + public required string WorkflowName { get; set; } +} + +/// Pointer to a GitHub repository. +/// The github_repository variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubRepository : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "github_repository"; + + /// Short description of the repository. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { 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; } + + /// Repository pointer. + [JsonPropertyName("repo")] + public required PushGitHubRepoRef Repo { get; set; } + + /// URL to the repository on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } +} + +/// One side of a file diff (head or base). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushAttachmentGitHubFileDiffSide +{ + /// 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; } +} + +/// 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)] +public partial class PushAttachmentGitHubFileDiff : PushAttachment +{ + /// + [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; } + + /// URL to the diff on GitHub (e.g., a commit, compare, or PR-file URL). + [JsonPropertyName("url")] + public required string Url { get; set; } +} + +/// One side of a tree comparison (head or base). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PushAttachmentGitHubTreeComparisonSide +{ + /// 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; +} + +/// Pointer to a comparison between two git revisions. +/// The github_tree_comparison variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubTreeComparison : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "github_tree_comparison"; + + /// Base side of the comparison. + [JsonPropertyName("base")] + public required PushAttachmentGitHubTreeComparisonSide Base { get; set; } + + /// 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; } +} + +/// Generic GitHub URL reference. +/// The github_url variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubUrl : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "github_url"; + + /// URL to the GitHub resource. + [JsonPropertyName("url")] + public required string Url { get; set; } +} + +/// Pointer to a file in a GitHub repository at a specific ref. +/// The github_file variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubFile : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "github_file"; + + /// 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 file on GitHub. + [JsonPropertyName("url")] + public required string Url { get; set; } +} + +/// Pointer to a line range inside a file in a GitHub repository. +/// The github_snippet variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentGitHubSnippet : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "github_snippet"; + + /// Line range the snippet covers. + [JsonPropertyName("lineRange")] + public required PushAttachmentFileLineRange LineRange { get; 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; } +} + +/// Blob attachment with inline base64-encoded data. +/// The blob variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentBlob : PushAttachment +{ + /// + [JsonIgnore] + public override string Type => "blob"; + + /// 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; } +} + +/// Slim input shape for extension_context attachments; identity fields are runtime-derived. +/// The extension_context variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PushAttachmentExtensionContext : PushAttachment +{ + /// + [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; } + + /// 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; } +} + +/// Parameters for session.extensions.sendAttachmentsToMessage. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SendAttachmentsToMessageParams +{ + /// 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; } + + /// 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; +} + +/// A tool name and arguments to execute through the session's native invocation pipeline. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ToolsExecuteRequest +{ + /// Arguments supplied to the tool. + [JsonPropertyName("arguments")] + public JsonElement Arguments { get; set; } + + /// Name of the currently offered tool to execute. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Optional identifier used to correlate this invocation with its tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } +} + +/// Custom grammar input format accepted by a built-in tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class BuiltinToolFormat +{ + /// Grammar definition accepted by the tool. + [JsonPropertyName("definition")] + public string Definition { get; set; } = string.Empty; + + /// Grammar syntax used by the format definition. + [JsonPropertyName("syntax")] + public string Syntax { get; set; } = string.Empty; + + /// Custom input-format discriminator. + [JsonPropertyName("type")] + public BuiltinToolFormatType Type { get; set; } +} + +/// JSON Schema object accepted by a built-in tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class BuiltinToolInputSchema +{ + /// Root type of the tool input schema. + [JsonPropertyName("type")] + public BuiltinToolInputSchemaType Type { get; set; } +} + +/// Rust-owned metadata and input schema for a built-in tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class BuiltinToolDescriptor +{ + /// 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; } + + /// JSON Schema for the tool input, or null when the tool uses a custom format. + [JsonPropertyName("inputSchema")] + public BuiltinToolInputSchema? InputSchema { get; set; } + + /// Optional supplemental usage instructions for the tool. + [JsonPropertyName("instructions")] + public string? Instructions { get; set; } + + /// 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; + + /// Policy describing which tool metadata may be recorded without obfuscation. + [JsonPropertyName("safeForTelemetry")] + public JsonElement SafeForTelemetry { get; set; } + + /// Optional human-readable title for the tool. + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// Optional tool category discriminator. + [JsonPropertyName("type")] + public string? Type { 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; } +} + +/// 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; } + + /// 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; + + /// Tool name used to start shell commands. + [JsonPropertyName("shellToolName")] + public string ShellToolName { get; set; } = string.Empty; + + /// Stable shell type identifier. + [JsonPropertyName("shellType")] + public string ShellType { get; set; } = string.Empty; + + /// Tool name used to stop shell commands. + [JsonPropertyName("stopShellToolName")] + public string StopShellToolName { get; set; } = string.Empty; +} + +/// 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; } + + /// Whether tool descriptors should include authoring metadata. + [JsonPropertyName("includeAuthor")] + public bool? IncludeAuthor { get; set; } + + /// Whether descriptors should favor fewer user-intervention prompts. + [JsonPropertyName("reduceUserIntervention")] + public bool? ReduceUserIntervention { 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class TaskCompleteData +{ + /// 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; } + + /// 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; } + + /// Summary of the completed task, provided by the agent. + [JsonPropertyName("summary")] + public string? Summary { get; set; } +} + +/// Binary result returned by a tool for the model. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ExternalToolTextResultForLlmBinaryResultsForLlm +{ + /// 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; } + + /// MIME type of the binary data. + [JsonPropertyName("mimeType")] + public string MimeType { get; set; } = string.Empty; + + /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. + [JsonPropertyName("type")] + public ExternalToolTextResultForLlmBinaryResultsForLlmType Type { 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; +} + + +/// 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"; + + /// The text content. + [JsonPropertyName("text")] + public required string Text { 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"; + + /// 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; } +} + +/// 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"; + + /// 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; } + + /// Whether outputPreview is known to be incomplete or truncated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputTruncated")] + public bool? OutputTruncated { get; set; } + + /// Shell id, as assigned by Copilot runtime. + [JsonPropertyName("shellId")] + public required string ShellId { get; set; } +} + +/// Image content block with base64-encoded data. +/// The image variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderConfig +public partial class ExternalToolTextResultForLlmContentImage : ExternalToolTextResultForLlmContent { - /// API key. Optional for local providers like Ollama. - [JsonPropertyName("apiKey")] - public string? ApiKey { get; set; } + /// + [JsonIgnore] + public override string Type => "image"; - /// Azure-specific provider options. - [JsonPropertyName("azure")] - public ProviderConfigAzure? Azure { get; set; } + /// Base64-encoded image data. + [Base64String] + [JsonPropertyName("data")] + public required string Data { get; set; } - /// API endpoint URL. - [JsonPropertyName("baseUrl")] - public string BaseUrl { get; set; } = string.Empty; + /// MIME type of the image (e.g., image/png, image/jpeg). + [JsonPropertyName("mimeType")] + public required string MimeType { get; set; } +} - /// Bearer token for authentication. Sets the Authorization header directly. Takes precedence over apiKey when both are set. - [JsonPropertyName("bearerToken")] - public string? BearerToken { 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"; - /// 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; } + /// Base64-encoded audio data. + [Base64String] + [JsonPropertyName("data")] + public required string Data { get; set; } - /// Custom HTTP headers to include in all outbound requests to the provider. - [JsonPropertyName("headers")] - public IDictionary? Headers { get; set; } + /// MIME type of the audio (e.g., audio/wav, audio/mpeg). + [JsonPropertyName("mimeType")] + public required string MimeType { get; set; } +} - /// Maximum context window tokens for the model. - [JsonPropertyName("maxContextWindowTokens")] - public double? MaxContextWindowTokens { 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; } - /// Maximum output tokens for the model. - [JsonPropertyName("maxOutputTokens")] - public double? MaxOutputTokens { get; set; } + /// Available icon sizes (e.g., ['16x16', '32x32']). + [JsonPropertyName("sizes")] + public IList? Sizes { get; set; } - /// Maximum prompt/input tokens for the model. - [JsonPropertyName("maxPromptTokens")] - public double? MaxPromptTokens { get; set; } + /// URL or path to the icon image. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; - /// Overrides for model capabilities when they cannot be inferred from modelId. - [JsonPropertyName("modelCapabilities")] - public ModelCapabilitiesOverride? ModelCapabilities { get; set; } + /// Theme variant this icon is intended for. + [JsonPropertyName("theme")] + public ExternalToolTextResultForLlmContentResourceLinkIconTheme? Theme { 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; } +/// 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"; - /// 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; } + /// Human-readable description of the resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Provider name used for model and telemetry attribution. - [JsonPropertyName("providerName")] - public string? ProviderName { get; set; } + /// Icons associated with this resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("icons")] + public IList? Icons { get; set; } - /// Provider transport. Defaults to "http". - [JsonPropertyName("transport")] - public ProviderConfigTransport? Transport { get; set; } + /// MIME type of the resource content. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } - /// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. - [JsonPropertyName("type")] - public ProviderConfigType? Type { get; set; } + /// Resource name identifier. + [JsonPropertyName("name")] + public required string Name { get; set; } - /// Wire API format (openai/azure only). Defaults to "completions". - [JsonPropertyName("wireApi")] - public ProviderConfigWireApi? WireApi { get; set; } + /// Size of the resource in bytes. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("size")] + public long? Size { 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; } + /// Human-readable display title for the resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// URI identifying the resource. + [JsonPropertyName("uri")] + public required string Uri { 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. -/// +/// 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"; + + /// The embedded resource contents, either text or base64-encoded binary. + [JsonPropertyName("resource")] + public required JsonElement Resource { 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; + + /// Source attributed to the injected message. + [JsonPropertyName("source")] + public string Source { get; set; } = string.Empty; +} + +/// 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; } + + /// 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; } + + /// Information-flow metadata captured from the completion reviewer. + [JsonPropertyName("reviewerResultMeta")] + public JsonElement? ReviewerResultMeta { 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; } + + /// 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; } + + /// Detailed log content available for session display. + [JsonPropertyName("sessionLog")] + public string? SessionLog { get; set; } + + /// Skill invocation metadata produced by the tool. + [JsonPropertyName("skillInvocation")] + public JsonElement? SkillInvocation { get; set; } + + /// Whether large-output post-processing should be skipped. + [JsonPropertyName("skipLargeOutputProcessing")] + public bool? SkipLargeOutputProcessing { get; set; } + + /// Structured result content in addition to the model-facing text. + [JsonPropertyName("structuredContent")] + public JsonElement? StructuredContent { get; set; } + + /// Completion-review decision produced by the task-completion tool. + [JsonPropertyName("taskCompletionDecision")] + public TaskCompletionDecision? TaskCompletionDecision { get; set; } + + /// Text result returned to the model. + [JsonPropertyName("textResultForLlm")] + public string TextResultForLlm { get; set; } = string.Empty; + + /// Deferred tool names made available by this result. + [JsonPropertyName("toolReferences")] + public IList? ToolReferences { get; set; } + + /// Tool-specific telemetry payload. + [JsonPropertyName("toolTelemetry")] + public JsonElement? ToolTelemetry { get; set; } + + /// Optional UI resource produced by the tool. + [JsonPropertyName("uiResource")] + public JsonElement? UiResource { get; set; } +} + +/// Task-completion tool arguments and final result used to build a label-safe session event payload. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ToolsTaskCompleteEventDataRequest +{ + /// Final expanded result returned by the task_complete tool. + [JsonPropertyName("finalResult")] + public ToolResultExpanded FinalResult { 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; } +} + +/// Indicates whether the external tool call result was handled successfully. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HandlePendingToolCallResult +{ + /// Whether the tool call result was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// 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 HandlePendingToolCallRequest +{ + /// Error message if the tool call failed. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Request ID of the pending tool call. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Tool call result (string or expanded result object). + [JsonPropertyName("result")] + public JsonElement? Result { get; 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PendingExternalToolRequest +{ + /// 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; } + + /// Arguments to pass to the external tool. + [JsonPropertyName("arguments")] + public JsonElement? Arguments { get; set; } + + /// 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; } + + /// Request ID to pass to session.tools.handlePendingToolCall. + [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; + + /// Name of the external tool to invoke. + [JsonPropertyName("toolName")] + public string ToolName { get; set; } = string.Empty; +} + +/// External tool calls still waiting for a result. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PendingExternalToolRequestList +{ + /// 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; } +} + +/// 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)] +internal sealed class ToolsListPendingRequestsRequest +{ + /// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolsInitializeAndValidateResult +{ +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionToolsInitializeAndValidateRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Lightweight metadata for a currently initialized session tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CurrentToolMetadata +{ + /// Whether the tool is loaded on demand via tool search. + [JsonPropertyName("deferLoading")] + public bool? DeferLoading { get; set; } + + /// Tool description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// JSON Schema for tool input. + [JsonPropertyName("input_schema")] + public IDictionary? InputSchema { get; set; } + + /// MCP server name for MCP-backed tools. + [JsonPropertyName("mcpServerName")] + public string? McpServerName { 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; } +} + +/// Current lightweight tool metadata snapshot for the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolsGetCurrentMetadataResult +{ + /// Current tool metadata, or null when tools have not been initialized yet. + [JsonPropertyName("tools")] + public IList? Tools { get; set; } +} + +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionToolsGetCurrentMetadataRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Empty result after replacing the calling connection's externally implemented tools. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ToolsSetResult +{ +} + +/// Serializable definition of a caller-implemented tool whose execution is handled over the SDK connection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProtocolExternalToolDefinition +{ + /// 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; + + /// Whether the tool executes commands in a terminal. + [JsonPropertyName("isTerminal")] + public bool? IsTerminal { get; set; } + + /// Optional caller-defined metadata associated with the tool. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } + + /// Unique model-visible tool name. + [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; } +} + +/// 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)] -public sealed class ShellCredentials +internal sealed class ToolsSetRequest { - /// - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// - /// 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; } + /// Complete replacement list for the calling connection. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } } -/// A host-provided script sourced before each built-in shell command when its shell target matches the active shell. +/// Empty result after applying subagent settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellInitScript +public sealed class ToolsUpdateSubagentSettingsResult { - /// 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. +/// Subagent model, reasoning effort, context tier, and auto-invocation settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellOptions +public sealed class SubagentSettingsEntry { - /// Command-scoped GitHub credential injection for shell commands. - [JsonPropertyName("credentials")] - public ShellCredentials? Credentials { get; set; } + /// 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; } - /// Controls automatic non-interactive profile loading where supported. Explicit initScripts are unaffected. - [JsonPropertyName("initProfile")] - public ShellInitProfile? InitProfile { get; set; } + /// Context tier override for matching subagents. + [JsonPropertyName("contextTier")] + public SubagentSettingsEntryContextTier? ContextTier { 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; } + /// Reasoning effort override for matching subagents. + [JsonPropertyName("effortLevel")] + public string? EffortLevel { 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; } + /// Model override for matching subagents. + [JsonPropertyName("model")] + public string? Model { get; set; } + + /// Whether the configured model strategy is preferred or required. + [JsonPropertyName("modelPolicy")] + public AgentModelPolicy? ModelPolicy { get; set; } } -/// Patch of mutable session options to apply to the running session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUpdateOptionsParams +/// Configured per-agent subagent overrides. +public sealed class UpdateSubagentSettingsRequestSubagents { - /// 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; } + /// Per-agent settings keyed by subagent agent_type. + [JsonPropertyName("agents")] + public IDictionary? Agents { get; set; } - /// Runtime context discriminator (e.g., `cli`, `actions`). - [JsonPropertyName("agentContext")] - public string? AgentContext { get; set; } + /// Names of subagents the user has turned off; they cannot be dispatched. + [JsonPropertyName("disabledSubagents")] + public IList? DisabledSubagents { 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; } + /// Maximum number of subagents that can run concurrently; applies to usage-based billing users only. + [JsonPropertyName("maxConcurrency")] + public int? MaxConcurrency { get; set; } - /// Whether to disable the `ask_user` tool (encourages autonomous behavior). - [JsonPropertyName("askUserDisabled")] - public bool? AskUserDisabled { get; set; } + /// Maximum subagent nesting depth; applies to usage-based billing users only. + [JsonPropertyName("maxDepth")] + public int? MaxDepth { get; set; } +} - /// Allowlist of tool names available to this session. - [JsonPropertyName("availableTools")] - public IList? AvailableTools { get; set; } +/// Subagent settings to apply to the current session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UpdateSubagentSettingsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Options scoped to the built-in CAPI (Copilot API) provider. - [JsonPropertyName("capi")] - public CapiSessionOptions? Capi { get; set; } + /// Subagent settings to apply, or null to clear the live session override. + [JsonPropertyName("subagents")] + public UpdateSubagentSettingsRequestSubagents? Subagents { get; set; } +} - /// Identifier of the client driving the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } +/// RPC data type for SessionCommandsList operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionCommandsListRequest +{ + /// Include runtime built-in commands. + [JsonPropertyName("includeBuiltins")] + public bool? IncludeBuiltins { get; set; } - /// Whether to include the `Co-authored-by` trailer in commit messages. - [JsonPropertyName("coauthorEnabled")] - public bool? CoauthorEnabled { get; set; } + /// Include commands registered by protocol clients, including SDK clients and extensions. + [JsonPropertyName("includeClientCommands")] + public bool? IncludeClientCommands { 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; } + /// Include enabled user-invocable skills and commands. + [JsonPropertyName("includeSkills")] + public bool? IncludeSkills { get; set; } +} - /// Whether to allow auto-mode continuation across turns. - [JsonPropertyName("continueOnAutoMode")] - public bool? ContinueOnAutoMode { get; set; } +/// RPC data type for SessionCommandsListRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionCommandsListRequestWithSession +{ + /// Include runtime built-in commands. + [JsonPropertyName("includeBuiltins")] + public bool? IncludeBuiltins { get; set; } - /// Override URL for the Copilot API endpoint. - [JsonPropertyName("copilotUrl")] - public string? CopilotUrl { get; set; } + /// Include commands registered by protocol clients, including SDK clients and extensions. + [JsonPropertyName("includeClientCommands")] + public bool? IncludeClientCommands { get; set; } - /// Whether to default custom agents to local-only execution. - [JsonPropertyName("customAgentsLocalOnly")] - public bool? CustomAgentsLocalOnly { get; set; } + /// Include enabled user-invocable skills and commands. + [JsonPropertyName("includeSkills")] + public bool? IncludeSkills { get; set; } - /// Instruction source IDs to exclude from the system prompt. - [JsonPropertyName("disabledInstructionSources")] - public IList? DisabledInstructionSources { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Skill IDs that should be excluded from this session. - [JsonPropertyName("disabledSkills")] - public IList? DisabledSkills { get; set; } +/// 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)] +[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 +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK callback hook mechanism. - [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; } +/// 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 SlashCommandInvocationResultText : SlashCommandInvocationResult +{ + /// + [JsonIgnore] + public override string Kind => "text"; - /// 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 text contains Markdown. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("markdown")] + public bool? Markdown { get; set; } - /// Whether to surface reasoning-summary events from the model. - [JsonPropertyName("enableReasoningSummaries")] - public bool? EnableReasoningSummaries { get; set; } + /// Whether ANSI sequences should be preserved. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("preserveAnsi")] + public bool? PreserveAnsi { get; set; } - /// Whether shell-script safety heuristics are enabled. - [JsonPropertyName("enableScriptSafety")] - public bool? EnableScriptSafety { 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; } - /// Whether to enable cross-session store writes and reads. - [JsonPropertyName("enableSessionStore")] - public bool? EnableSessionStore { 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; } - /// 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; } + /// Text output for the client to render. + [JsonPropertyName("text")] + public required string Text { get; set; } +} - /// Whether to stream model responses. - [JsonPropertyName("enableStreaming")] - public bool? EnableStreaming { 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 . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SlashCommandInvocationResultAgentPrompt : SlashCommandInvocationResult +{ + /// + [JsonIgnore] + public override string Kind => "agent-prompt"; - /// How env values are passed to MCP servers (`direct` inlines literal values; `indirect` resolves at launch). - [JsonPropertyName("envValueMode")] - public OptionsUpdateEnvValueMode? EnvValueMode { get; set; } + /// Prompt text to display to the user. + [JsonPropertyName("displayPrompt")] + public required string DisplayPrompt { 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; } + /// Optional target session mode for the agent prompt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mode")] + public SessionMode? Mode { get; set; } - /// Whether subagent callback events should be forwarded into the session event log sink. - [JsonPropertyName("eventsLogIncludesSubagents")] - public bool? EventsLogIncludesSubagents { get; set; } + /// Optional user-facing notice to show before the prompt is submitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("notice")] + public string? Notice { 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; } + /// Prompt to submit to the agent. + [JsonPropertyName("prompt")] + public required string Prompt { get; set; } - /// Denylist of tool names for this session. - [JsonPropertyName("excludedTools")] - public IList? ExcludedTools { 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; } +} - /// Map of feature-flag IDs to their boolean enabled state. - [JsonPropertyName("featureFlags")] - public IDictionary? FeatureFlags { get; set; } +/// 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 SlashCommandInvocationResultCompleted : SlashCommandInvocationResult +{ + /// + [JsonIgnore] + public override string Kind => "completed"; - /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { get; set; } + /// Optional user-facing message describing the completed command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { 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 target session mode applied without submitting an agent prompt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mode")] + public SessionMode? Mode { 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; } + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { 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; } +/// Selectable slash-command subcommand option with name, description, and optional group label. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SlashCommandSelectSubcommandOption +{ + /// Human-readable description of the subcommand. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Stable integration identifier used for analytics and rate-limit attribution. - [JsonPropertyName("integrationId")] - public string? IntegrationId { get; set; } + /// Optional group label for organizing options. + [JsonPropertyName("group")] + public string? Group { get; set; } - /// Whether experimental capabilities are enabled. - [JsonPropertyName("isExperimentalMode")] - public bool? IsExperimentalMode { get; set; } + /// Subcommand name to invoke. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} - /// Whether interactive shell sessions are logged. - [JsonPropertyName("logInteractiveShells")] - public bool? LogInteractiveShells { get; set; } +/// 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 Kind => "select-subcommand"; - /// Identifier sent to LSP-style integrations. - [JsonPropertyName("lspClientName")] - public string? LspClientName { get; set; } + /// Parent command name that requires subcommand selection. + [JsonPropertyName("command")] + public required string Command { 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; } + /// Available subcommand options for the client to present. + [JsonPropertyName("options")] + public required IList Options { 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; } + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } - /// The model ID to use for assistant turns. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Human-readable title for the selection UI. + [JsonPropertyName("title")] + public required string Title { get; set; } +} - /// Per-property model capability overrides for the selected model. - [JsonPropertyName("modelCapabilitiesOverrides")] - public ModelCapabilitiesOverride? ModelCapabilitiesOverrides { get; set; } +/// RPC data type for SlashCommandTimelineEntry operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SlashCommandTimelineEntry +{ + /// 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; } - /// Organization-level custom instructions to inject into the system prompt. - [JsonPropertyName("organizationCustomInstructions")] - public string? OrganizationCustomInstructions { get; set; } + /// Text displayed for the timeline entry. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; - /// Custom model-provider configuration (BYOK). - [JsonPropertyName("provider")] - public ProviderConfig? Provider { get; set; } + /// Timeline entry presentation type. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; - /// 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; } + /// Optional URL associated with the timeline entry. + [JsonPropertyName("url")] + public string? Url { get; set; } +} - /// Reasoning summary mode for supported model clients. - [JsonPropertyName("reasoningSummary")] - public OptionsUpdateReasoningSummary? ReasoningSummary { get; set; } +/// The add-timeline-entry variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SlashCommandInvocationResultAddTimelineEntry : SlashCommandInvocationResult +{ + /// + [JsonIgnore] + public override string Kind => "add-timeline-entry"; - /// Whether the session is running in an interactive UI. - [JsonPropertyName("runningInInteractiveMode")] - public bool? RunningInInteractiveMode { get; set; } + /// Timeline entry the host should append. + [JsonPropertyName("entry")] + public required SlashCommandTimelineEntry Entry { get; set; } - /// Resolved sandbox configuration. - [JsonPropertyName("sandboxConfig")] - public SandboxConfig? SandboxConfig { get; set; } + /// Optional text the host should prefill into the input editor. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("prefillInput")] + public string? PrefillInput { 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; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { 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; } +/// RPC data type for SlashCommandModelPickerDialog operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SlashCommandModelPickerDialog +{ + /// Discriminator for a model-picker dialog. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Model that should be enabled before it can be selected. + [JsonPropertyName("modelToEnable")] + public string? ModelToEnable { get; set; } - /// Optional session limits. Pass null to clear the session limits. - [JsonPropertyName("sessionLimits")] - public SessionLimitsConfig? SessionLimits { get; set; } + /// Settings scope the picker should modify. + [JsonPropertyName("scope")] + public string? Scope { get; set; } - /// Per-session settings for built-in shell tools. - [JsonPropertyName("shell")] - public ShellOptions? Shell { get; set; } + /// Model-selection target represented by the picker. + [JsonPropertyName("target")] + public string? Target { get; set; } +} + +/// The show-dialog variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SlashCommandInvocationResultShowDialog : SlashCommandInvocationResult +{ + /// + [JsonIgnore] + public override string Kind => "show-dialog"; + + /// Dialog the host should display. + [JsonPropertyName("dialog")] + public required SlashCommandModelPickerDialog Dialog { 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 command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } +} - /// PowerShell process flags applied to built-in and user-requested shell commands. - [JsonPropertyName("shellProcessFlags")] - public IList? ShellProcessFlags { get; set; } +/// The set-model variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class SlashCommandInvocationResultSetModel : SlashCommandInvocationResult +{ + /// + [JsonIgnore] + public override string Kind => "set-model"; - /// Additional directories to search for skills. - [JsonPropertyName("skillDirectories")] - public IList? SkillDirectories { 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; } - /// Whether to skip loading custom instruction sources. - [JsonPropertyName("skipCustomInstructions")] - public bool? SkipCustomInstructions { get; set; } + /// Model selected by the command. + [JsonPropertyName("model")] + public required string Model { get; set; } - /// Whether to skip embedding retrieval pipeline initialization and execution. - [JsonPropertyName("skipEmbeddingRetrieval")] - public bool? SkipEmbeddingRetrieval { get; set; } + /// Reasoning effort selected for the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { 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; } + /// Repository settings scope modified by the command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("repoScope")] + public string? RepoScope { get; set; } - /// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. - [JsonPropertyName("toolFilterPrecedence")] - public OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence { 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 path for trajectory output. - [JsonPropertyName("trajectoryFile")] - public string? TrajectoryFile { get; set; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } - /// Output verbosity level for supported models. - [JsonPropertyName("verbosity")] - public Verbosity? Verbosity { get; set; } + /// Settings scope modified by the command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("scope")] + public string? Scope { get; set; } - /// Absolute working-directory path for shell tools. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { get; set; } + /// User-facing warning produced while selecting the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Parameters for (re)loading the merged LSP configuration set. +/// The set-plan-model variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class LspInitializeRequest +public partial class SlashCommandInvocationResultSetPlanModel : SlashCommandInvocationResult { - /// Force re-initialization even when LSP configs were already loaded for the working directory. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// + [JsonIgnore] + public override string Kind => "set-plan-model"; - /// Git root used as the boundary when traversing for project-level LSP configs (supports monorepos). - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } + /// User-facing confirmation message for the plan-model selection. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Dedicated model selected for plan mode. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("planModel")] + public string? PlanModel { 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; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Discovered extension metadata, including source-qualified ID, name, discovery source, status, and optional process ID. +/// Slash command name and optional raw input string to invoke. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Extension +internal sealed class CommandsInvokeRequest { - /// 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; + /// Raw input after the command name. + [JsonPropertyName("input")] + public string? Input { get; set; } - /// Extension name (directory name). + /// Command name. Leading slashes are stripped and the name is matched case-insensitively. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Process ID if the extension is running. - [JsonPropertyName("pid")] - public long? Pid { 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; } - - /// Current status: running, disabled, failed, or starting. - [JsonPropertyName("status")] - public ExtensionStatus Status { get; set; } -} - -/// Extensions discovered for the session, with their current status. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionList -{ - /// Discovered extensions and their current status. - [JsonPropertyName("extensions")] - public IList Extensions { get => field ??= []; set; } -} + /// Optional client surface that initiated the invocation. + [JsonPropertyName("origin")] + public CommandsInvocationOrigin? Origin { get; 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; } -/// Source-qualified extension identifier to enable for the session. +/// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExtensionsEnableRequest +internal sealed class CommandsFinalizeInvocationEffectResult { - /// Source-qualified extension ID to enable. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Failure reason when the invocation effect could not be finalized. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the pending invocation effect was finalized successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Source-qualified extension identifier to disable for the session. +/// The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExtensionsDisableRequest +internal sealed class CommandsFinalizeInvocationEffectRequest { - /// Source-qualified extension ID to disable. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// The slash-command result object that produced the pending effect, echoed back unchanged. + [JsonPropertyName("effect")] + public IDictionary Effect { get => field ??= new Dictionary(); 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; } -/// Identifies the target session. +/// Indicates whether the pending client-handled command was completed successfully. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionExtensionsReloadRequest +public sealed class CommandsHandlePendingCommandResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the command was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Identifies the target session. +/// Pending command request ID and an optional error if the client handler failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionExtensionsReconcileRequest +internal sealed class CommandsHandlePendingCommandRequest { + /// 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; } -/// Attachment union accepted by push input, covering files, directories, GitHub objects, blobs, snippets, and extension context. -/// Polymorphic base type discriminated by type. +/// Error message produced while executing the command, if any. [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 +public sealed class ExecuteCommandResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Error message produced while executing the command, if any. Omitted when the handler succeeded. + [JsonPropertyName("error")] + public string? Error { get; set; } } - -/// Optional line range to scope the attachment to a specific section of the file. +/// Slash command name and argument string to execute synchronously. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentFileLineRange +internal sealed class ExecuteCommandParams { - /// End line number (1-based, inclusive). - [JsonPropertyName("end")] - public long End { get; set; } + /// Argument string to pass to the command (empty string if none). + [JsonPropertyName("args")] + public string Args { get; set; } = string.Empty; - /// Start line number (1-based). - [JsonPropertyName("start")] - public long Start { get; set; } + /// Name of the slash command to invoke (without the leading '/'). + [JsonPropertyName("commandName")] + public string CommandName { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// File attachment. -/// The file 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 PushAttachmentFile : PushAttachment +public partial class EnqueueCommandResult { - /// - [JsonIgnore] - public override string Type => "file"; - - /// User-facing display name for the attachment. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// The boolean discriminator. + [JsonPropertyName("queued")] + public bool Queued { get; set; } - /// Optional line range to scope the attachment to a specific section of the file. + /// Stable opaque ID of the queued command. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("lineRange")] - public PushAttachmentFileLineRange? LineRange { get; set; } - - /// Absolute file path. - [JsonPropertyName("path")] - public required string Path { get; set; } + [JsonPropertyName("queueId")] + public string? QueueId { get; set; } } -/// Directory attachment. -/// The directory variant of . +/// Slash-prefixed command string to enqueue for FIFO processing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentDirectory : PushAttachment +internal sealed class EnqueueCommandParams { - /// - [JsonIgnore] - public override string Type => "directory"; + /// 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; - /// User-facing display name for the attachment. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// Optional user-facing text for the queue row. The command string is shown when omitted. + [JsonPropertyName("displayText")] + public string? DisplayText { get; set; } - /// Absolute directory path. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// End position of the selection. +/// Indicates whether the queued-command response was matched to a pending request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentSelectionDetailsEnd +public sealed class CommandsRespondToQueuedCommandResult { - /// 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; } + /// 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; } } -/// Start position of the selection. +/// Result of the queued command execution. +/// Data type discriminated by handled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentSelectionDetailsStart +public partial class QueuedCommandResult { - /// Start character offset within the line (0-based). - [JsonPropertyName("character")] - public long Character { get; set; } + /// The boolean discriminator. + [JsonPropertyName("handled")] + public bool Handled { get; set; } - /// Start line number (0-based). - [JsonPropertyName("line")] - public long Line { get; set; } + /// When true, the runtime will not process subsequent queued commands until a new request comes in. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("stopProcessingQueue")] + public bool? StopProcessingQueue { get; set; } } -/// Position range of the selection within the file. +/// 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)] -public sealed class PushAttachmentSelectionDetails +internal sealed class CommandsRespondToQueuedCommandRequest { - /// End position of the selection. - [JsonPropertyName("end")] - public PushAttachmentSelectionDetailsEnd End { get => field ??= new(); set; } + /// Request ID from the `command.queued` event the host is responding to. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Start position of the selection. - [JsonPropertyName("start")] - public PushAttachmentSelectionDetailsStart Start { get => field ??= new(); set; } + /// Result of the queued command execution. + [JsonPropertyName("result")] + public QueuedCommandResult Result { 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 . +/// Telemetry engagement ID for the session, when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentSelection : PushAttachment +public sealed class SessionTelemetryEngagement { - /// - [JsonIgnore] - public override string Type => "selection"; - - /// User-facing display name for the selection. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } - - /// Absolute path to the file containing the selection. - [JsonPropertyName("filePath")] - public required string FilePath { get; set; } - - /// Position range of the selection within the file. - [JsonPropertyName("selection")] - public required PushAttachmentSelectionDetails Selection { get; set; } - - /// The selected text content. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Current telemetry engagement ID, when available. + [JsonPropertyName("engagementId")] + public string? EngagementId { get; set; } } -/// GitHub issue, pull request, or discussion reference. -/// The github_reference variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubReference : PushAttachment +internal sealed class SessionTelemetryGetEngagementIdRequest { - /// - [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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Pointer to a GitHub repository. +/// Feature override key/value pairs to attach to subsequent telemetry events from this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushGitHubRepoRef +internal sealed class TelemetrySetFeatureOverridesRequest { - /// Numeric GitHub repository id. - [JsonPropertyName("id")] - public long? Id { get; set; } - - /// Repository name (without owner). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } - /// 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 . +/// 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 partial class PushAttachmentGitHubCommit : PushAttachment +public sealed class UIEphemeralQueryResult { - /// - [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; } + /// Answer returned by the model. + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; } -/// Pointer to a GitHub release. -/// The github_release variant of . +/// Transient question to answer without adding it to conversation history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubRelease : PushAttachment +internal sealed class UIEphemeralQueryRequest { - /// - [JsonIgnore] - public override string Type => "github_release"; - - /// Human-readable release name. - [JsonPropertyName("name")] - public required string Name { get; set; } - - /// Repository the release belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } - - /// Git tag the release is anchored to. - [JsonPropertyName("tagName")] - public required string TagName { get; set; } + /// Question to answer from the current conversation context. + [JsonPropertyName("question")] + public string Question { get; set; } = string.Empty; - /// URL to the release on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Pointer to a GitHub Actions job. -/// The github_actions_job variant of . +/// The elicitation response (accept with form values, decline, or cancel). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubActionsJob : PushAttachment +public sealed class UIElicitationResponse { - /// - [JsonIgnore] - public override string Type => "github_actions_job"; - - /// 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; } - - /// Job id within the workflow run. - [JsonPropertyName("jobId")] - public required long JobId { get; set; } - - /// Display name of the job. - [JsonPropertyName("jobName")] - public required string JobName { get; set; } - - /// Repository the workflow run belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// MCP response metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// URL to the job on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// The user's response: accept (submitted), decline (rejected), or cancel (dismissed). + [JsonPropertyName("action")] + public UIElicitationResponseAction Action { get; set; } - /// Display name of the workflow the job ran in. - [JsonPropertyName("workflowName")] - public required string WorkflowName { get; set; } + /// The form values submitted by the user (present when action is 'accept'). + [JsonPropertyName("content")] + public IDictionary? Content { get; set; } } -/// Pointer to a GitHub repository. -/// The github_repository variant of . +/// JSON Schema describing the form fields to present to the user. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubRepository : PushAttachment +public sealed class UIElicitationSchema { - /// - [JsonIgnore] - public override string Type => "github_repository"; - - /// Short description of the repository. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { 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; } + /// Form field definitions, keyed by field name. + [JsonPropertyName("properties")] + public IDictionary Properties { get => field ??= new Dictionary(); set; } - /// Repository pointer. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// List of required field names. + [JsonPropertyName("required")] + public IList? Required { get; set; } - /// URL to the repository on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Schema type indicator (always 'object'). + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; } -/// One side of a file diff (head or base). +/// Metadata controlling an MCP task's lifetime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentGitHubFileDiffSide +public sealed class McpTaskMetadata { - /// 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; } + /// Task time-to-live. + [JsonPropertyName("ttl")] + public long? Ttl { get; set; } } -/// Pointer to a single-file diff. At least one of `head` and `base` must be present. -/// The github_file_diff variant of . +/// Prompt message and JSON schema describing the form fields to elicit from the user. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubFileDiff : PushAttachment +internal sealed class UIElicitationRequest { - /// - [JsonIgnore] - public override string Type => "github_file_diff"; + /// MCP request metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// File location on the base side of the diff. Absent for additions. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("base")] - public PushAttachmentGitHubFileDiffSide? Base { get; set; } + /// Message describing what information is needed from the user. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// File location on the head side of the diff. Absent for deletions. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("head")] - public PushAttachmentGitHubFileDiffSide? Head { get; set; } + /// Elicitation mode. Omitted and form are equivalent for structured elicitation. + [JsonPropertyName("mode")] + public McpElicitationFormMode? Mode { 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; } + /// 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; + + /// MCP task metadata. + [JsonPropertyName("task")] + public McpTaskMetadata? Task { get; set; } } -/// One side of a tree comparison (head or base). +/// 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 PushAttachmentGitHubTreeComparisonSide +public sealed class UIElicitationResult { - /// 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; + /// Whether the response was accepted. False if the request was already resolved by another client. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Pointer to a comparison between two git revisions. -/// The github_tree_comparison variant of . +/// Pending elicitation request ID and the user's response (accept/decline/cancel + form values). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubTreeComparison : PushAttachment +internal sealed class UIHandlePendingElicitationRequest { - /// - [JsonIgnore] - public override string Type => "github_tree_comparison"; + /// The unique request ID from the elicitation.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Base side of the comparison. - [JsonPropertyName("base")] - public required PushAttachmentGitHubTreeComparisonSide Base { get; set; } + /// The elicitation response (accept with form values, decline, or cancel). + [JsonPropertyName("result")] + public UIElicitationResponse Result { get => field ??= new(); set; } - /// Head side of the comparison. - [JsonPropertyName("head")] - public required PushAttachmentGitHubTreeComparisonSide Head { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// URL to the comparison on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } +/// Indicates whether the pending UI request was resolved by this call. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class UIHandlePendingResult +{ + /// 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; } } -/// Generic GitHub URL reference. -/// The github_url variant of . +/// User response for a pending user-input request, with answer text and whether it was typed freeform. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubUrl : PushAttachment +public sealed class UIUserInputResponse { - /// - [JsonIgnore] - public override string Type => "github_url"; + /// The user's answer text. + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; - /// URL to the GitHub resource. - [JsonPropertyName("url")] - public required string Url { 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; } } -/// Pointer to a file in a GitHub repository at a specific ref. -/// The github_file variant of . +/// Request ID of a pending `user_input.requested` event and the user's response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubFile : PushAttachment +internal sealed class UIHandlePendingUserInputRequest { - /// - [JsonIgnore] - public override string Type => "github_file"; + /// The unique request ID from the user_input.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// 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; } - /// Git ref the file is read at (branch, tag, or commit SHA). - [JsonPropertyName("ref")] - public required string Ref { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Repository the file lives in. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } +/// Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIHandlePendingSamplingRequest +{ + /// The unique request ID from the sampling.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// URL to the file on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// 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; } -/// Pointer to a line range inside a file in a GitHub repository. -/// The github_snippet variant of . +/// Request ID of a pending `auto_mode_switch.requested` event and the user's response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubSnippet : PushAttachment +internal sealed class UIHandlePendingAutoModeSwitchRequest { - /// - [JsonIgnore] - public override string Type => "github_snippet"; + /// The unique request ID from the auto_mode_switch.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Line range the snippet covers. - [JsonPropertyName("lineRange")] - public required PushAttachmentFileLineRange LineRange { get; set; } + /// 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; } - /// 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; } +/// The user's selected action for an exhausted session limit. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class UISessionLimitsExhaustedResponse +{ + /// Action selected by the user. + [JsonPropertyName("action")] + public UISessionLimitsExhaustedResponseAction Action { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// AI Credits to add to the current max when action is 'add'. + [JsonPropertyName("additionalAiCredits")] + public double? AdditionalAiCredits { get; set; } - /// URL to the snippet on GitHub (with line anchor). - [JsonPropertyName("url")] - public required string Url { get; set; } + /// New absolute max AI Credits when action is 'set'. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } } -/// Blob attachment with inline base64-encoded data. -/// The blob variant of . +/// Request ID of a pending `session_limits_exhausted.requested` event and the user's selected limit action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentBlob : PushAttachment +internal sealed class UIHandlePendingSessionLimitsExhaustedRequest { - /// - [JsonIgnore] - public override string Type => "blob"; - - /// Base64-encoded content. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// The unique request ID from the session_limits_exhausted.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// User-facing display name for the attachment. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// The selected session-limit action. + [JsonPropertyName("response")] + public UISessionLimitsExhaustedResponse Response { get => field ??= new(); set; } - /// MIME type of the inline data. - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Slim input shape for extension_context attachments; identity fields are runtime-derived. -/// The extension_context variant of . +/// 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 partial class PushAttachmentExtensionContext : PushAttachment +public sealed class UIExitPlanModeResponse { - /// - [JsonIgnore] - public override string Type => "extension_context"; + /// Whether the plan was approved. + [JsonPropertyName("approved")] + public bool Approved { get; set; } - /// Caller-supplied JSON payload (required, may be null but not undefined). - [JsonPropertyName("payload")] - public required JsonElement Payload { get; set; } + /// Whether subsequent edits should be auto-approved without confirmation. + [JsonPropertyName("autoApproveEdits")] + public bool? AutoApproveEdits { 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; } + /// 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; } + + /// The action the user selected. Defaults to 'autopilot' when autoApproveEdits is true, otherwise 'interactive'. + [JsonPropertyName("selectedAction")] + public UIExitPlanModeAction? SelectedAction { get; set; } } -/// Parameters for session.extensions.sendAttachmentsToMessage. +/// Request ID of a pending `exit_plan_mode.requested` event and the user's response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SendAttachmentsToMessageParams +internal sealed class UIHandlePendingExitPlanModeRequest { - /// 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; } + /// The unique request ID from the exit_plan_mode.requested event. + [JsonPropertyName("requestId")] + public string RequestId { 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; } + /// 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; } -/// A tool name and arguments to execute through the session's native invocation pipeline. +/// 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)] -internal sealed class ToolsExecuteRequest +public sealed class UIRegisterDirectAutoModeSwitchHandlerResult { - /// Arguments supplied to the tool. - [JsonPropertyName("arguments")] - public JsonElement Arguments { get; set; } - - /// Name of the currently offered tool to execute. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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 SessionUiRegisterDirectAutoModeSwitchHandlerRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Optional identifier used to correlate this invocation with its tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } } -/// Custom grammar input format accepted by a built-in tool. +/// Indicates whether the handle was active and the registration count was decremented. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolFormat +public sealed class UIUnregisterDirectAutoModeSwitchHandlerResult { - /// Grammar definition accepted by the tool. - [JsonPropertyName("definition")] - public string Definition { get; set; } = string.Empty; + /// True if the handle was active and decremented the counter; false if the handle was unknown. + [JsonPropertyName("unregistered")] + public bool Unregistered { get; set; } +} - /// Grammar syntax used by the format definition. - [JsonPropertyName("syntax")] - public string Syntax { get; set; } = string.Empty; +/// Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest +{ + /// Handle previously returned by `registerDirectAutoModeSwitchHandler`. + [JsonPropertyName("handle")] + public string Handle { get; set; } = string.Empty; - /// Custom input-format discriminator. - [JsonPropertyName("type")] - public BuiltinToolFormatType Type { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// JSON Schema object accepted by a built-in tool. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolInputSchema +public sealed class PermissionsConfigureResult { - /// Root type of the tool input schema. - [JsonPropertyName("type")] - public BuiltinToolInputSchemaType Type { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Rust-owned metadata and input schema for a built-in tool. +/// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolDescriptor +public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource { - /// 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; } - - /// JSON Schema for the tool input, or null when the tool uses a custom format. - [JsonPropertyName("inputSchema")] - public BuiltinToolInputSchema? InputSchema { get; set; } - - /// Optional supplemental usage instructions for the tool. - [JsonPropertyName("instructions")] - public string? Instructions { get; set; } - - /// Whether the tool executes commands in a terminal. - [JsonPropertyName("isTerminal")] - public bool IsTerminal { get; set; } - - /// Stable name used to invoke the built-in tool. + /// Name of the policy source. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Policy describing which tool metadata may be recorded without obfuscation. - [JsonPropertyName("safeForTelemetry")] - public JsonElement SafeForTelemetry { get; set; } - - /// Optional human-readable title for the tool. - [JsonPropertyName("title")] - public string? Title { get; set; } - - /// Optional tool category discriminator. + /// Type of the policy source. [JsonPropertyName("type")] - public string? Type { 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; } + public string Type { get; set; } = string.Empty; } -/// Shell-specific names and description lines used to materialize built-in shell tool descriptors. +/// Single content-exclusion rule supplied to `session.permissions.configure`, with paths, match conditions, and source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsShellDescriptorConfig +public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRule { - /// Additional model-facing shell description lines. - [JsonPropertyName("descriptionLines")] - public IList DescriptionLines { get => field ??= []; set; } + /// Conditions of which at least one must match. + [JsonPropertyName("ifAnyMatch")] + public IList? IfAnyMatch { get; set; } - /// Human-readable shell name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Conditions none of which may match. + [JsonPropertyName("ifNoneMatch")] + public IList? IfNoneMatch { get; set; } - /// Tool name used to list active shells. - [JsonPropertyName("listShellsToolName")] - public string ListShellsToolName { get; set; } = string.Empty; + /// Path patterns covered by this rule. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } - /// Tool name used to read shell output. - [JsonPropertyName("readShellToolName")] - public string ReadShellToolName { get; set; } = string.Empty; + /// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. + [JsonPropertyName("source")] + public PermissionsConfigureAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } +} - /// Tool name used to start shell commands. - [JsonPropertyName("shellToolName")] - public string ShellToolName { get; set; } = string.Empty; +/// Content-exclusion policy supplied to `session.permissions.configure`, with rules, last-updated data, and scope. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsConfigureAdditionalContentExclusionPolicy +{ + /// Opaque policy update timestamp supplied by the host. + [JsonPropertyName("last_updated_at")] + public JsonElement LastUpdatedAt { get; set; } - /// Stable shell type identifier. - [JsonPropertyName("shellType")] - public string ShellType { get; set; } = string.Empty; + /// Content-exclusion rules to apply. + [JsonPropertyName("rules")] + public IList Rules { get => field ??= []; set; } - /// Tool name used to stop shell commands. - [JsonPropertyName("stopShellToolName")] - public string StopShellToolName { get; set; } = string.Empty; + /// Allowed values for the `PermissionsConfigureAdditionalContentExclusionPolicyScope` enumeration. + [JsonPropertyName("scope")] + public PermissionsConfigureAdditionalContentExclusionPolicyScope Scope { get; set; } } -/// Options controlling how Rust-owned built-in tool descriptors are materialized. +/// 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)] -internal sealed class ToolsGetBuiltinDescriptorsRequest +public sealed class PermissionPathsConfig { - /// Whether background task completion notifications are enabled. - [JsonPropertyName("backgroundTaskNotificationsEnabled")] - public bool? BackgroundTaskNotificationsEnabled { 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; } - /// Whether tool descriptors should include authoring metadata. - [JsonPropertyName("includeAuthor")] - public bool? IncludeAuthor { 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; } - /// Whether descriptors should favor fewer user-intervention prompts. - [JsonPropertyName("reduceUserIntervention")] - public bool? ReduceUserIntervention { 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } +} - /// Shell-specific names and description lines for shell tools. - [JsonPropertyName("shellConfig")] - public ToolsShellDescriptorConfig? ShellConfig { get; set; } +/// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionRulesSet +{ + /// Rules that auto-approve matching requests. + [JsonPropertyName("approved")] + public IList Approved { get => field ??= []; set; } - /// Whether the configured shell supports PowerShell 7 syntax. - [JsonPropertyName("shellSupportsPowerShell7Syntax")] - public bool? ShellSupportsPowerShell7Syntax { get; set; } + /// Rules that auto-deny matching requests. + [JsonPropertyName("denied")] + public IList Denied { get => field ??= []; set; } +} - /// Default shell timeout in milliseconds. - [JsonPropertyName("shellTimeoutMs")] - public double? ShellTimeoutMs { get; set; } +/// 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 PermissionUrlsConfig +{ + /// Initial list of allowed URL/domain patterns. Patterns may include path components. Ignored when `unrestricted` is true. + [JsonPropertyName("initialAllowed")] + public IList? InitialAllowed { get; set; } - /// Whether semantic skill lookup is available. - [JsonPropertyName("skillEmbeddingEnabled")] - public bool? SkillEmbeddingEnabled { 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; } } -/// Task completion notification with summary from the agent. +/// Patch of permission policy fields to apply (omit a field to leave it unchanged). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskCompleteData +internal sealed class PermissionsConfigureParams { - /// Structured blocker details when outcome is blocked. - [JsonPropertyName("blocker")] - public TaskBlocker? Blocker { 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; } - /// Active autopilot objective ID evaluated by the completion reviewer. - [JsonPropertyName("objectiveId")] - public long? ObjectiveId { 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; } - /// Semantic completion decision. Absent on legacy events and invalid tool calls. - [JsonPropertyName("outcome")] - public TaskCompletionOutcome? Outcome { 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; } - /// 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; } + /// 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; } - /// 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; } + /// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. + [JsonPropertyName("rules")] + public PermissionRulesSet? Rules { get; set; } - /// Summary of the completed task, provided by the agent. - [JsonPropertyName("summary")] - public string? Summary { 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; } } -/// Binary result returned by a tool for the model. +/// Indicates whether the permission decision was applied; false when the request was already resolved. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExternalToolTextResultForLlmBinaryResultsForLlm +public sealed class PermissionRequestResult { - /// 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; } - - /// MIME type of the binary data. - [JsonPropertyName("mimeType")] - public string MimeType { get; set; } = string.Empty; - - /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. - [JsonPropertyName("type")] - public ExternalToolTextResultForLlmBinaryResultsForLlmType Type { get; set; } + /// Whether the permission request was handled successfully. + [JsonPropertyName("success")] + public bool Success { 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. +/// The client's response to the pending permission prompt. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", + TypeDiscriminatorPropertyName = "kind", 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 +[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("type")] - public virtual string Type { get; set; } = string.Empty; + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Plain text content block. -/// 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 ExternalToolTextResultForLlmContentText : ExternalToolTextResultForLlmContent +public partial class PermissionDecisionApproveOnce : PermissionDecision { /// [JsonIgnore] - public override string Type => "text"; + public override string Kind => "approve-once"; - /// The text content. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// 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; } } -/// Terminal/shell output content block with optional exit code and working directory. -/// The terminal variant of . +/// 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)] -public partial class ExternalToolTextResultForLlmContentTerminal : ExternalToolTextResultForLlmContent +[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 { - /// - [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; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Shell command exit metadata with optional output preview. -/// The shell_exit variant of . + +/// Session-scoped approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentShellExit : ExternalToolTextResultForLlmContent +public partial class PermissionDecisionApproveForSessionApprovalCommands : PermissionDecisionApproveForSessionApproval { /// [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; } - - /// Whether outputPreview is known to be incomplete or truncated. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("outputTruncated")] - public bool? OutputTruncated { get; set; } + public override string Kind => "commands"; - /// Shell id, as assigned by Copilot runtime. - [JsonPropertyName("shellId")] - public required string ShellId { get; set; } + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } } -/// Image content block with base64-encoded data. -/// The image variant of . +/// Session-scoped approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentImage : ExternalToolTextResultForLlmContent +public partial class PermissionDecisionApproveForSessionApprovalRead : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Type => "image"; - - /// Base64-encoded image data. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } - - /// MIME type of the image (e.g., image/png, image/jpeg). - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + public override string Kind => "read"; } -/// Audio content block with base64-encoded data. -/// The audio variant of . +/// Session-scoped approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentAudio : ExternalToolTextResultForLlmContent +public partial class PermissionDecisionApproveForSessionApprovalWrite : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Type => "audio"; - - /// Base64-encoded audio data. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } - - /// MIME type of the audio (e.g., audio/wav, audio/mpeg). - [JsonPropertyName("mimeType")] - public required string MimeType { 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; } - - /// 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; } + public override string Kind => "write"; } -/// Resource link content block referencing an external resource. -/// The resource_link variant of . +/// 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 partial class ExternalToolTextResultForLlmContentResourceLink : ExternalToolTextResultForLlmContent +public partial class PermissionDecisionApproveForSessionApprovalMcp : PermissionDecisionApproveForSessionApproval { /// [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; } - - /// MIME type of the resource content. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Resource name identifier. - [JsonPropertyName("name")] - public required string Name { get; set; } - - /// Size of the resource in bytes. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("size")] - public long? Size { get; set; } + public override string Kind => "mcp"; - /// Human-readable display title for the resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("title")] - public string? Title { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } - /// URI identifying the resource. - [JsonPropertyName("uri")] - public required string Uri { get; set; } + /// MCP tool name, or null to cover every tool on the server. + [JsonPropertyName("toolName")] + public string? ToolName { get; set; } } -/// Embedded resource content block with inline text or binary data. -/// The resource 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 ExternalToolTextResultForLlmContentResource : ExternalToolTextResultForLlmContent +public partial class PermissionDecisionApproveForSessionApprovalMcpSampling : PermissionDecisionApproveForSessionApproval { /// [JsonIgnore] - public override string Type => "resource"; + public override string Kind => "mcp-sampling"; - /// The embedded resource contents, either text or base64-encoded binary. - [JsonPropertyName("resource")] - public required JsonElement Resource { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// A message injected by a tool result. +/// Session-scoped approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolResultNewMessage +public partial class PermissionDecisionApproveForSessionApprovalMemory : PermissionDecisionApproveForSessionApproval { - /// 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; + /// + [JsonIgnore] + public override string Kind => "memory"; } -/// RPC data type for TaskCompletionDecision operations. +/// Session-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 TaskCompletionDecision +public partial class PermissionDecisionApproveForSessionApprovalCustomTool : PermissionDecisionApproveForSessionApproval { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "custom-tool"; - /// Information-flow metadata captured from the completion reviewer. - [JsonPropertyName("reviewerResultMeta")] - public JsonElement? ReviewerResultMeta { get; set; } + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// Expanded canonical result returned by a session tool. +/// 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 ToolResultExpanded +public partial class PermissionDecisionApproveForSessionApprovalExtensionManagement : PermissionDecisionApproveForSessionApproval { - /// 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; } - - /// Detailed log content available for session display. - [JsonPropertyName("sessionLog")] - public string? SessionLog { get; set; } - - /// Skill invocation metadata produced by the tool. - [JsonPropertyName("skillInvocation")] - public JsonElement? SkillInvocation { get; set; } - - /// Whether large-output post-processing should be skipped. - [JsonPropertyName("skipLargeOutputProcessing")] - public bool? SkipLargeOutputProcessing { get; set; } - - /// Structured result content in addition to the model-facing text. - [JsonPropertyName("structuredContent")] - public JsonElement? StructuredContent { get; set; } - - /// Completion-review decision produced by the task-completion tool. - [JsonPropertyName("taskCompletionDecision")] - public TaskCompletionDecision? TaskCompletionDecision { get; set; } - - /// Text result returned to the model. - [JsonPropertyName("textResultForLlm")] - public string TextResultForLlm { get; set; } = string.Empty; - - /// Deferred tool names made available by this result. - [JsonPropertyName("toolReferences")] - public IList? ToolReferences { get; set; } - - /// Tool-specific telemetry payload. - [JsonPropertyName("toolTelemetry")] - public JsonElement? ToolTelemetry { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-management"; - /// Optional UI resource produced by the tool. - [JsonPropertyName("uiResource")] - public JsonElement? UiResource { 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; } } -/// Task-completion tool arguments and final result used to build a label-safe session event payload. +/// Session-scoped workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsTaskCompleteEventDataRequest +public partial class PermissionDecisionApproveForSessionApprovalWorkflow : PermissionDecisionApproveForSessionApproval { - /// Final expanded result returned by the task_complete tool. - [JsonPropertyName("finalResult")] - public ToolResultExpanded FinalResult { get => field ??= new(); set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "workflow"; - /// Arguments supplied to the completed task_complete tool call. - [JsonPropertyName("toolArgs")] - public JsonElement ToolArgs { get; set; } + /// 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; } } -/// Indicates whether the external tool call result was handled successfully. +/// 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 sealed class HandlePendingToolCallResult +public partial class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess : PermissionDecisionApproveForSessionApproval { - /// Whether the tool call result was handled successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// Pending external tool call request ID, with the tool result or an error describing why it failed. +/// 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)] -internal sealed class HandlePendingToolCallRequest +public partial class PermissionDecisionApproveForSessionApprovalExtensionEnvAccess : PermissionDecisionApproveForSessionApproval { - /// Error message if the tool call failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Request ID of the pending tool call. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "extension-env-access"; - /// Tool call result (string or expanded result object). - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Names of the sensitive environment variables this approval covers. Values are never persisted. + [JsonPropertyName("environmentVariables")] + public required IList EnvironmentVariables { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// External tool call of the session or one of its sub-agents that is still waiting for session.tools.handlePendingToolCall. +/// 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 sealed class PendingExternalToolRequest +public partial class PermissionDecisionApproveForSession : PermissionDecision { - /// 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; } - - /// Arguments to pass to the external tool. - [JsonPropertyName("arguments")] - public JsonElement? Arguments { get; set; } + /// + [JsonIgnore] + public override string Kind => "approve-for-session"; - /// 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; } + /// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("approval")] + public PermissionDecisionApproveForSessionApproval? Approval { get; set; } - /// Request ID to pass to session.tools.handlePendingToolCall. - [JsonPropertyName("requestId")] - public string RequestId { 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; } +} - /// Tool call ID assigned to this external tool invocation. - [JsonPropertyName("toolCallId")] - public string ToolCallId { 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 . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PermissionDecisionApproveReadOnlyForSession : PermissionDecision +{ + /// + [JsonIgnore] + public override string Kind => "approve-read-only-for-session"; - /// Name of the external tool to invoke. - [JsonPropertyName("toolName")] - public string ToolName { get; set; } = string.Empty; + /// Canonical directories covered by the read-only grant. + [JsonPropertyName("directories")] + public required IList Directories { get; set; } } -/// External tool calls still waiting for a result. +/// Approval to persist for this location. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PendingExternalToolRequestList +[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 { - /// 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 type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { 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. + +/// Location-scoped approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsListPendingRequestsRequest +public partial class PermissionDecisionApproveForLocationApprovalCommands : PermissionDecisionApproveForLocationApproval { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "commands"; + + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } } -/// 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. +/// Location-scoped approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsInitializeAndValidateResult +public partial class PermissionDecisionApproveForLocationApprovalRead : PermissionDecisionApproveForLocationApproval { + /// + [JsonIgnore] + public override string Kind => "read"; } -/// Identifies the target session. +/// Location-scoped approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionToolsInitializeAndValidateRequest +public partial class PermissionDecisionApproveForLocationApprovalWrite : PermissionDecisionApproveForLocationApproval { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "write"; } -/// Lightweight metadata for a currently initialized session tool. +/// 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)] -public sealed class CurrentToolMetadata +public partial class PermissionDecisionApproveForLocationApprovalMcp : PermissionDecisionApproveForLocationApproval { - /// Whether the tool is loaded on demand via tool search. - [JsonPropertyName("deferLoading")] - public bool? DeferLoading { get; set; } - - /// Tool description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// JSON Schema for tool input. - [JsonPropertyName("input_schema")] - public IDictionary? InputSchema { get; set; } - - /// MCP server name for MCP-backed tools. - [JsonPropertyName("mcpServerName")] - public string? McpServerName { get; set; } - - /// Raw MCP tool name for MCP-backed tools. - [JsonPropertyName("mcpToolName")] - public string? McpToolName { get; set; } + /// + [JsonIgnore] + public override string Kind => "mcp"; - /// Model-facing tool name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } - /// Optional MCP/config namespaced tool name. - [JsonPropertyName("namespacedName")] - public string? NamespacedName { get; set; } + /// MCP tool name, or null to cover every tool on the server. + [JsonPropertyName("toolName")] + public string? ToolName { get; set; } } -/// Current lightweight tool metadata snapshot for the session. +/// Location-scoped approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsGetCurrentMetadataResult +public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : PermissionDecisionApproveForLocationApproval { - /// Current tool metadata, or null when tools have not been initialized yet. - [JsonPropertyName("tools")] - public IList? Tools { get; set; } + /// + [JsonIgnore] + public override string Kind => "mcp-sampling"; + + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// Identifies the target session. +/// Location-scoped approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionToolsGetCurrentMetadataRequest +public partial class PermissionDecisionApproveForLocationApprovalMemory : PermissionDecisionApproveForLocationApproval { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "memory"; } -/// Empty result after replacing the calling connection's externally implemented tools. +/// 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 ToolsSetResult +public partial class PermissionDecisionApproveForLocationApprovalCustomTool : PermissionDecisionApproveForLocationApproval { + /// + [JsonIgnore] + public override string Kind => "custom-tool"; + + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// Serializable definition of a caller-implemented tool whose execution is handled over the SDK connection. +/// Location-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 ProtocolExternalToolDefinition +public partial class PermissionDecisionApproveForLocationApprovalExtensionManagement : PermissionDecisionApproveForLocationApproval { - /// 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; - - /// Whether the tool executes commands in a terminal. - [JsonPropertyName("isTerminal")] - public bool? IsTerminal { get; set; } - - /// Optional caller-defined metadata associated with the tool. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } - - /// Unique model-visible tool name. - [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; } + /// + [JsonIgnore] + public override string Kind => "extension-management"; - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { 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; } } -/// Complete externally implemented tool list for the calling connection. An empty list removes every tool previously supplied by that connection. +/// Location-scoped workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsSetRequest +public partial class PermissionDecisionApproveForLocationApprovalWorkflow : PermissionDecisionApproveForLocationApproval { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "workflow"; - /// Complete replacement list for the calling connection. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// 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; } } -/// Empty result after applying subagent settings. +/// 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 ToolsUpdateSubagentSettingsResult +public partial class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess : PermissionDecisionApproveForLocationApproval { + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// Subagent model, reasoning effort, context tier, and auto-invocation settings. +/// 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)] -public sealed class SubagentSettingsEntry +public partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAccess : PermissionDecisionApproveForLocationApproval { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "extension-env-access"; - /// Model override for matching subagents. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Names of the sensitive environment variables this approval covers. Values are never persisted. + [JsonPropertyName("environmentVariables")] + public required IList EnvironmentVariables { get; set; } - /// Whether the configured model strategy is preferred or required. - [JsonPropertyName("modelPolicy")] - public AgentModelPolicy? ModelPolicy { get; set; } + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// Configured per-agent subagent overrides. -public sealed class UpdateSubagentSettingsRequestSubagents +/// 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 partial class PermissionDecisionApproveForLocation : PermissionDecision { - /// Per-agent settings keyed by subagent agent_type. - [JsonPropertyName("agents")] - public IDictionary? Agents { get; set; } - - /// Names of subagents the user has turned off; they cannot be dispatched. - [JsonPropertyName("disabledSubagents")] - public IList? DisabledSubagents { get; set; } + /// + [JsonIgnore] + public override string Kind => "approve-for-location"; - /// Maximum number of subagents that can run concurrently; applies to usage-based billing users only. - [JsonPropertyName("maxConcurrency")] - public int? MaxConcurrency { get; set; } + /// Approval to persist for this location. + [JsonPropertyName("approval")] + public required PermissionDecisionApproveForLocationApproval Approval { get; set; } - /// Maximum subagent nesting depth; applies to usage-based billing users only. - [JsonPropertyName("maxDepth")] - public int? MaxDepth { get; set; } + /// Location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public required string LocationKey { get; set; } } -/// Subagent settings to apply to the current session. +/// 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 UpdateSubagentSettingsRequest +public partial class PermissionDecisionApprovePermanently : PermissionDecision { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "approve-permanently"; - /// Subagent settings to apply, or null to clear the live session override. - [JsonPropertyName("subagents")] - public UpdateSubagentSettingsRequestSubagents? Subagents { get; set; } + /// URL domain to approve permanently. + [JsonPropertyName("domain")] + public required string Domain { get; set; } } -/// RPC data type for SessionCommandsList operations. +/// 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 SessionCommandsListRequest +public partial class PermissionDecisionReject : PermissionDecision { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "reject"; - /// Include enabled user-invocable skills and commands. - [JsonPropertyName("includeSkills")] - public bool? IncludeSkills { get; set; } + /// Optional feedback explaining the rejection. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } } -/// RPC data type for SessionCommandsListRequestWithSession operations. +/// Permission-decision variant indicating no user was available to confirm the request. +/// The user-not-available variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCommandsListRequestWithSession +public partial class PermissionDecisionUserNotAvailable : PermissionDecision { - /// 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; } - - /// Include enabled user-invocable skills and commands. - [JsonPropertyName("includeSkills")] - public bool? IncludeSkills { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "user-not-available"; } -/// Result of invoking the slash command (text output, prompt to send to the agent, completion, or subcommand selection). -/// Polymorphic base type discriminated by kind. +/// Permission-decision variant indicating the request was approved. +/// The approved variant of . [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 +public partial class PermissionDecisionApproved : PermissionDecision { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "approved"; } +/// 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)] +public partial class PermissionDecisionApprovedForSession : PermissionDecision +{ + /// + [JsonIgnore] + public override string Kind => "approved-for-session"; + + /// The approval to add as a session-scoped rule. + [JsonPropertyName("approval")] + public required UserToolSessionApproval Approval { get; set; } +} -/// Slash-command invocation result containing text output plus Markdown/ANSI rendering flags. -/// The text variant of . +/// 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 SlashCommandInvocationResultText : SlashCommandInvocationResult +public partial class PermissionDecisionApprovedForLocation : PermissionDecision { /// [JsonIgnore] - public override string Kind => "text"; + public override string Kind => "approved-for-location"; - /// Whether text contains Markdown. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("markdown")] - public bool? Markdown { get; set; } + /// The approval to persist for this location. + [JsonPropertyName("approval")] + public required UserToolSessionApproval Approval { get; set; } - /// Whether ANSI sequences should be preserved. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("preserveAnsi")] - public bool? PreserveAnsi { get; set; } + /// The location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public required string LocationKey { 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; } +/// 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 partial class PermissionDecisionCancelled : PermissionDecision +{ + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// 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. + /// Optional explanation of why the request was cancelled. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("sandboxSessionChange")] - public SandboxSessionChange? SandboxSessionChange { get; set; } + [JsonPropertyName("reason")] + public string? Reason { get; set; } +} - /// Text output for the client to render. - [JsonPropertyName("text")] - public required string Text { get; 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"; + + /// Rules that denied the request. + [JsonPropertyName("rules")] + public required IList Rules { 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 . +/// 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 SlashCommandInvocationResultAgentPrompt : SlashCommandInvocationResult +public partial class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser : PermissionDecision { /// [JsonIgnore] - public override string Kind => "agent-prompt"; - - /// Prompt text to display to the user. - [JsonPropertyName("displayPrompt")] - public required string DisplayPrompt { get; set; } + public override string Kind => "denied-no-approval-rule-and-could-not-request-from-user"; +} - /// Optional target session mode for the agent prompt. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mode")] - public SessionMode? Mode { 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"; - /// Optional user-facing notice to show before the prompt is submitted. + /// Optional feedback from the user explaining the denial. [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; } + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + /// Whether to force-reject the current agent turn. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + [JsonPropertyName("forceReject")] + public bool? ForceReject { get; set; } } -/// Slash-command invocation result indicating completion, with optional message and settings-change flag. -/// The completed variant of . +/// 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)] -public partial class SlashCommandInvocationResultCompleted : SlashCommandInvocationResult +public partial class PermissionDecisionDeniedByContentExclusionPolicy : PermissionDecision { /// [JsonIgnore] - public override string Kind => "completed"; + public override string Kind => "denied-by-content-exclusion-policy"; - /// Optional user-facing message describing the completed command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Human-readable explanation of why the path was excluded. [JsonPropertyName("message")] - public string? Message { get; set; } + public required string Message { get; set; } - /// Optional target session mode applied without submitting an agent prompt. + /// File path that triggered the exclusion. + [JsonPropertyName("path")] + public required string Path { 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"; + + /// Whether to interrupt the current agent turn. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mode")] - public SessionMode? Mode { get; set; } + [JsonPropertyName("interrupt")] + public bool? Interrupt { get; set; } - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. + /// Optional message from the hook explaining the denial. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// Selectable slash-command subcommand option with name, description, and optional group label. +/// Pending permission request ID and the decision to apply (approve/reject and scope). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandSelectSubcommandOption +internal sealed class PermissionDecisionRequest { - /// Human-readable description of the subcommand. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// 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; } - /// Optional group label for organizing options. - [JsonPropertyName("group")] - public string? Group { get; set; } + /// Request ID of the pending permission request. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Subcommand name to invoke. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } -/// Slash-command invocation result asking the client to present subcommand options for a parent command. -/// The select-subcommand variant of . +/// Pending permission prompt reconstructed from event history, with request ID and user-facing prompt details. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSelectSubcommand : SlashCommandInvocationResult +public sealed class PendingPermissionRequest { - /// - [JsonIgnore] - public override string Kind => "select-subcommand"; + /// Permission-recovery episode that authorized this request to surface for interactive attention. + [JsonPropertyName("recoveryEpisodeId")] + public string? RecoveryEpisodeId { get; set; } - /// Parent command name that requires subcommand selection. - [JsonPropertyName("command")] - public required string Command { 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!; - /// Available subcommand options for the client to present. - [JsonPropertyName("options")] - public required IList Options { get; set; } + /// Unique identifier for the pending permission request. + [JsonPropertyName("requestId")] + public string RequestId { 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; } +/// 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; } +} - /// Human-readable title for the selection UI. - [JsonPropertyName("title")] - public required string Title { get; 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; } -/// RPC data type for SlashCommandTimelineEntry operations. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandTimelineEntry +public sealed class PermissionsSetApproveAllResult { - /// 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; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Text displayed for the timeline entry. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; +/// Allow-all toggle for tool permission requests, with an optional telemetry source. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionsSetApproveAllRequest +{ + /// Whether to auto-approve all tool permission requests. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Timeline entry presentation type. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Optional URL associated with the timeline entry. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. + [JsonPropertyName("source")] + public PermissionsSetApproveAllSource? Source { get; set; } } -/// The add-timeline-entry variant of . +/// Indicates whether the requested permission mode was applied and reports the authoritative post-mutation mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultAddTimelineEntry : SlashCommandInvocationResult +public sealed class PermissionsSetModeResult { - /// - [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; } + /// Authoritative permission mode after the mutation. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// RPC data type for SlashCommandModelPickerDialog operations. +/// Permission mode to apply for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandModelPickerDialog +internal sealed class PermissionsSetModeRequest { - /// Discriminator for a model-picker dialog. - [JsonPropertyName("kind")] - public string Kind { 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; } - /// Model that should be enabled before it can be selected. - [JsonPropertyName("modelToEnable")] - public string? ModelToEnable { get; set; } + /// Permission mode to apply. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } - /// Settings scope the picker should modify. - [JsonPropertyName("scope")] - public string? Scope { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Model-selection target represented by the picker. - [JsonPropertyName("target")] - public string? Target { get; set; } + /// 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; } } -/// The show-dialog variant of . +/// Current permission mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultShowDialog : SlashCommandInvocationResult +public sealed class PermissionsGetModeResult { - /// - [JsonIgnore] - public override string Kind => "show-dialog"; - - /// Dialog the host should display. - [JsonPropertyName("dialog")] - public required SlashCommandModelPickerDialog Dialog { get; set; } + /// Current permission mode. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } +} - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } +/// No parameters. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionsGetModeRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// User-settings snapshot to restore if the host cancels the model switch. -public sealed class SlashCommandInvocationResultSetModelRevertOnCancel +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsModifyRulesResult { + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// The set-model variant of . +/// Scope and add/remove instructions for modifying session- or location-scoped permission rules. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSetModel : SlashCommandInvocationResult +internal sealed class PermissionsModifyRulesParams { - /// - [JsonIgnore] - public override string Kind => "set-model"; + /// Rules to add to the scope. Applied before `remove`/`removeAll`. + [JsonPropertyName("add")] + public IList? Add { 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; } + /// Specific rules to remove from the scope. Ignored when `removeAll` is true. + [JsonPropertyName("remove")] + public IList? Remove { get; set; } - /// Model selected by the command. - [JsonPropertyName("model")] - public required string Model { 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; } - /// Reasoning effort selected for the model. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { 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; } - /// Repository settings scope modified by the command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("repoScope")] - public string? RepoScope { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// User-settings snapshot to restore if the host cancels the model switch. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("revertOnCancel")] - public SlashCommandInvocationResultSetModelRevertOnCancel? RevertOnCancel { get; set; } +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsSetRequiredResult +{ + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { 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 +{ + /// 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; } - /// Settings scope modified by the command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("scope")] - public string? Scope { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// User-facing warning produced while selecting the model. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("warning")] - public string? Warning { get; set; } +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsResetSessionApprovalsResult +{ + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// The set-plan-model variant of . +/// Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSetPlanModel : SlashCommandInvocationResult +internal sealed class PermissionsResetSessionApprovalsRequest { - /// - [JsonIgnore] - public override string Kind => "set-plan-model"; + /// Whether location-scoped approvals and exact session-approved paths are cleared too. Defaults to `true`. + [JsonPropertyName("includeLocation")] + public bool? IncludeLocation { get; set; } - /// User-facing confirmation message for the plan-model selection. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Dedicated model selected for plan mode. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("planModel")] - public string? PlanModel { get; set; } +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsNotifyPromptShownResult +{ + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } +/// Notification payload describing the permission prompt that the client just rendered. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionPromptShownNotification +{ + /// 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; } -/// Slash command name and optional raw input string to invoke. +/// Snapshot of the session's recursive directory grants, exact session-approved paths, and primary working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsInvokeRequest +public sealed class PermissionPathsList { - /// Raw input after the command name. - [JsonPropertyName("input")] - public string? Input { get; set; } + /// All directories currently allowed for tool access on this session. + [JsonPropertyName("directories")] + public IList Directories { get => field ??= []; set; } - /// Command name. Leading slashes are stripped and the name is matched case-insensitively. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The primary working directory for this session. + [JsonPropertyName("primary")] + public string Primary { get; set; } = string.Empty; - /// Optional client surface that initiated the invocation. - [JsonPropertyName("origin")] - public CommandsInvocationOrigin? Origin { get; set; } + /// Exact paths approved for this session without recursively allowing their descendants. + [JsonPropertyName("sessionApprovedPaths")] + public IList? SessionApprovedPaths { get; set; } +} +/// No parameters; returns the session's recursive directory grants and exact session-approved paths. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionsPathsListRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsFinalizeInvocationEffectResult +public sealed class PermissionsPathsAddResult { - /// 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. + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// The slash-command result object that produced the pending effect, echoed back unchanged. -public sealed class CommandsFinalizeInvocationEffectRequestEffect +/// Directory path to add to the session's allowed directories. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionPathsAddParams { + /// 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; } -/// The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsFinalizeInvocationEffectRequest +public sealed class PermissionsPathsUpdatePrimaryResult { - /// The slash-command result object that produced the pending effect, echoed back unchanged. - [JsonPropertyName("effect")] - public CommandsFinalizeInvocationEffectRequestEffect Effect { get => field ??= new(); set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Whether the host applied or cancelled the pending invocation effect. - [JsonPropertyName("outcome")] - public CommandsInvocationEffectOutcome Outcome { get; set; } +/// Directory path to set as the session's new primary working directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionPathsUpdatePrimaryParams +{ + /// 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; } -/// Indicates whether the pending client-handled command was completed successfully. +/// Indicates whether the supplied path is within the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandsHandlePendingCommandResult +public sealed class PermissionPathsAllowedCheckResult { - /// Whether the command was handled successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the path is within the session's allowed directories. + [JsonPropertyName("allowed")] + public bool Allowed { get; set; } } -/// Pending command request ID and an optional error if the client handler failed. +/// Path to evaluate against the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsHandlePendingCommandRequest +internal sealed class PermissionPathsAllowedCheckParams { - /// 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; + /// Path to check against the session's allowed directories. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Error message produced while executing the command, if any. +/// Indicates whether the supplied path is within the session's workspace directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExecuteCommandResult +public sealed class PermissionPathsWorkspaceCheckResult { - /// Error message produced while executing the command, if any. Omitted when the handler succeeded. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Whether the path is within the session workspace directory. + [JsonPropertyName("allowed")] + public bool Allowed { get; set; } +} + +/// Path to evaluate against the session's workspace (primary) directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionPathsWorkspaceCheckParams +{ + /// Path to check against the session workspace directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Slash command name and argument string to execute synchronously. +/// Resolved location-permissions key and type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExecuteCommandParams +public sealed class PermissionLocationResolveResult { - /// Argument string to pass to the command (empty string if none). - [JsonPropertyName("args")] - public string Args { get; set; } = string.Empty; + /// Location key used in the location-permissions store. + [JsonPropertyName("locationKey")] + public string LocationKey { get; set; } = string.Empty; - /// Name of the slash command to invoke (without the leading '/'). - [JsonPropertyName("commandName")] - public string CommandName { get; set; } = string.Empty; + /// Whether the location is a git repo or directory. + [JsonPropertyName("locationType")] + public PermissionLocationType LocationType { get; set; } +} +/// Working directory to resolve into a location-permissions key. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionLocationResolveParams +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Working directory whose permission location should be resolved. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Indicates whether the command was accepted into the local execution queue. -/// Data type discriminated by queued. +/// Summary of persisted location permissions applied to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class EnqueueCommandResult +public sealed class PermissionLocationApplyResult { - /// The boolean discriminator. - [JsonPropertyName("queued")] - public bool Queued { get; set; } + /// Number of persisted allowed directories added to the live path manager. + [JsonPropertyName("appliedDirectoryCount")] + public long AppliedDirectoryCount { get; set; } - /// Stable opaque ID of the queued command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("queueId")] - public string? QueueId { get; set; } + /// Number of location-scoped rules added to the live permission service. + [JsonPropertyName("appliedRuleCount")] + public long AppliedRuleCount { get; set; } + + /// Location-scoped rules applied to the live permission service. + [JsonPropertyName("appliedRules")] + public IList AppliedRules { get => field ??= []; set; } + + /// Whether a different location was applied since the previous apply call. + [JsonPropertyName("changed")] + public bool Changed { 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; } } -/// Slash-prefixed command string to enqueue for FIFO processing. +/// Working directory to load persisted location permissions for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EnqueueCommandParams +internal sealed class PermissionLocationApplyParams { - /// 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; + + /// Working directory whose persisted location permissions should be applied. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Indicates whether the queued-command response was matched to a pending request. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandsRespondToQueuedCommandResult +public sealed class PermissionsLocationsAddToolApprovalResult { - /// Whether a pending queued command with the given request ID was found and resolved. False when the request was already resolved, cancelled, or unknown. + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// Result of the queued command execution. -/// Data type discriminated by handled. +/// Tool approval to persist and apply. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class QueuedCommandResult +[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 { - /// The boolean discriminator. - [JsonPropertyName("handled")] - public bool Handled { get; set; } - - /// When true, the runtime will not process subsequent queued commands until a new request comes in. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("stopProcessingQueue")] - public bool? StopProcessingQueue { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands). + +/// Location-persisted tool approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsRespondToQueuedCommandRequest +public partial class PermissionsLocationsAddToolApprovalDetailsCommands : PermissionsLocationsAddToolApprovalDetails { - /// Request ID from the `command.queued` event the host is responding to. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "commands"; - /// Result of the queued command execution. - [JsonPropertyName("result")] - public QueuedCommandResult Result { get => field ??= new(); set; } + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// Location-persisted tool approval details for read-only filesystem operations. +/// The read variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PermissionsLocationsAddToolApprovalDetailsRead : PermissionsLocationsAddToolApprovalDetails +{ + /// + [JsonIgnore] + public override string Kind => "read"; } -/// Telemetry engagement ID for the session, when available. +/// Location-persisted tool approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionTelemetryEngagement +public partial class PermissionsLocationsAddToolApprovalDetailsWrite : PermissionsLocationsAddToolApprovalDetails { - /// Current telemetry engagement ID, when available. - [JsonPropertyName("engagementId")] - public string? EngagementId { get; set; } + /// + [JsonIgnore] + public override string Kind => "write"; } -/// Identifies the target session. +/// 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)] -internal sealed class SessionTelemetryGetEngagementIdRequest +public partial class PermissionsLocationsAddToolApprovalDetailsMcp : PermissionsLocationsAddToolApprovalDetails { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [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; } } -/// Feature override key/value pairs to attach to subsequent telemetry events from this session. +/// Location-persisted tool approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TelemetrySetFeatureOverridesRequest +public partial class PermissionsLocationsAddToolApprovalDetailsMcpSampling : PermissionsLocationsAddToolApprovalDetails { - /// 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 => "mcp-sampling"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// Completed transient query. Ordered chunks and the terminal outcome are also delivered through `ui.ephemeral_query` session events while it runs. +/// Location-persisted tool approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIEphemeralQueryResult +public partial class PermissionsLocationsAddToolApprovalDetailsMemory : PermissionsLocationsAddToolApprovalDetails { - /// Answer returned by the model. - [JsonPropertyName("answer")] - public string Answer { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "memory"; } -/// Transient question to answer without adding it to conversation history. +/// Location-persisted tool approval details for a custom tool, keyed by tool name. +/// The custom-tool variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIEphemeralQueryRequest +public partial class PermissionsLocationsAddToolApprovalDetailsCustomTool : PermissionsLocationsAddToolApprovalDetails { - /// Question to answer from the current conversation context. - [JsonPropertyName("question")] - public string Question { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "custom-tool"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// The elicitation response (accept with form values, decline, or cancel). +/// 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 sealed class UIElicitationResponse +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManagement : PermissionsLocationsAddToolApprovalDetails { - /// 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 => "extension-management"; - /// The form values submitted by the user (present when action is 'accept'). - [JsonPropertyName("content")] - public IDictionary? Content { 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; } } -/// JSON Schema describing the form fields to present to the user. +/// Location-persisted workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationSchema +public partial class PermissionsLocationsAddToolApprovalDetailsWorkflow : PermissionsLocationsAddToolApprovalDetails { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "workflow"; - /// Schema type indicator (always 'object'). - [JsonPropertyName("type")] - public string Type { 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; } } -/// Metadata controlling an MCP task's lifetime. +/// 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 sealed class McpTaskMetadata +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess : PermissionsLocationsAddToolApprovalDetails { - /// Task time-to-live. - [JsonPropertyName("ttl")] - public long? Ttl { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// Prompt message and JSON schema describing the form fields to elicit from the user. +/// 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)] -internal sealed class UIElicitationRequest +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess : PermissionsLocationsAddToolApprovalDetails { - /// MCP request metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-env-access"; - /// Message describing what information is needed from the user. - [JsonPropertyName("message")] - public string Message { 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; } - /// Elicitation mode. Omitted and form are equivalent for structured elicitation. - [JsonPropertyName("mode")] - public McpElicitationFormMode? Mode { get; set; } + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } +} - /// JSON Schema describing the form fields to present to the user. - [JsonPropertyName("requestedSchema")] - public UIElicitationSchema RequestedSchema { get => field ??= new(); set; } +/// Location-scoped tool approval to persist. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionLocationAddToolApprovalParams +{ + /// Tool approval to persist and apply. + [JsonPropertyName("approval")] + public PermissionsLocationsAddToolApprovalDetails Approval { get => field ??= new(); set; } + + /// Location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public string LocationKey { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; +} - /// MCP task metadata. - [JsonPropertyName("task")] - public McpTaskMetadata? Task { get; set; } +/// Folder path to check for trust. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class FolderTrustCheckParams +{ + /// Folder path to check. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the elicitation response was accepted; false if it was already resolved by another client. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationResult +public sealed class PermissionsFolderTrustAddTrustedResult { - /// Whether the response was accepted. False if the request was already resolved by another client. + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// Pending elicitation request ID and the user's response (accept/decline/cancel + form values). +/// Folder path to add to trusted folders. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingElicitationRequest +internal sealed class FolderTrustAddParams { - /// 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; } + /// 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; } -/// Indicates whether the pending UI request was resolved by this call. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIHandlePendingResult +public sealed class PermissionsUrlsSetUnrestrictedModeResult { - /// 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. + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { 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; - - /// 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 of a pending `user_input.requested` event and the user's response. +/// Whether the URL-permission policy should run in unrestricted mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingUserInputRequest +internal sealed class PermissionUrlsSetUnrestrictedModeParams { - /// The unique request ID from the user_input.requested event. - [JsonPropertyName("requestId")] - public string RequestId { 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; } + /// 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; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. +/// The repository the remote session targets. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIHandlePendingSamplingResponse +public sealed class MetadataSnapshotRemoteMetadataRepository { + /// The branch the remote session is operating on. + [JsonPropertyName("branch")] + public string Branch { get; set; } = string.Empty; + + /// The GitHub repository name (without owner). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// The GitHub owner (user or organization) of the target repository. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject). +/// 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)] -internal sealed class UIHandlePendingSamplingRequest +public sealed class MetadataSnapshotRemoteMetadata { - /// The unique request ID from the sampling.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// The pull request number the remote session is associated with, if any. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. - [JsonPropertyName("response")] - public UIHandlePendingSamplingResponse? Response { get; set; } + /// The repository the remote session targets. + [JsonPropertyName("repository")] + public MetadataSnapshotRemoteMetadataRepository Repository { get => field ??= new(); set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// Whether the remote task originated from Copilot Coding Agent (cca) or a CLI `--remote` invocation. + [JsonPropertyName("taskType")] + public MetadataSnapshotRemoteMetadataTaskType? TaskType { get; set; } } -/// Request ID of a pending `auto_mode_switch.requested` event and the user's response. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingAutoModeSwitchRequest +/// Public-facing projection of workspace metadata for SDK / TUI consumers. +public sealed class SessionMetadataSnapshotWorkspace { - /// The unique request ID from the auto_mode_switch.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Branch checked out at session start, if any. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// 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; } + /// ISO 8601 timestamp when the workspace was created. + [JsonPropertyName("created_at")] + public DateTimeOffset? CreatedAt { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// 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; } } -/// The user's selected action for an exhausted session limit. +/// Point-in-time snapshot of slow-changing session identifier and state fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UISessionLimitsExhaustedResponse +public sealed class SessionMetadataSnapshot { - /// Action selected by the user. - [JsonPropertyName("action")] - public UISessionLimitsExhaustedResponseAction Action { get; set; } + /// 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; } - /// AI Credits to add to the current max when action is 'add'. - [JsonPropertyName("additionalAiCredits")] - public double? AdditionalAiCredits { get; set; } + /// Runtime client name associated with the session (telemetry identifier). + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } - /// New absolute max AI Credits when action is 'set'. - [JsonPropertyName("maxAiCredits")] - public double? MaxAiCredits { 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; } -/// 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 UIHandlePendingSessionLimitsExhaustedRequest -{ - /// The unique request ID from the session_limits_exhausted.requested event. - [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 selected session-limit action. - [JsonPropertyName("response")] - public UISessionLimitsExhaustedResponse Response { 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; -} -/// 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 UIExitPlanModeResponse -{ - /// Whether the plan was approved. - [JsonPropertyName("approved")] - public bool Approved { get; set; } + /// Current session limits, or null when no limits are active. + [JsonPropertyName("sessionLimits")] + public SessionLimitsConfig? SessionLimits { get; set; } - /// Whether subsequent edits should be auto-approved without confirmation. - [JsonPropertyName("autoApproveEdits")] - public bool? AutoApproveEdits { get; set; } + /// ISO 8601 timestamp of when the session started. + [JsonPropertyName("startTime")] + public DateTimeOffset StartTime { 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; } + /// Short human-readable summary of the session, if known. Omitted when no summary has been generated. + [JsonPropertyName("summary")] + public string? Summary { get; set; } - /// Feedback from the user when they declined the plan or requested changes. - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } + /// Absolute path to the session's current working directory. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; - /// The action the user selected. Defaults to 'autopilot' when autoApproveEdits is true, otherwise 'interactive'. - [JsonPropertyName("selectedAction")] - public UIExitPlanModeAction? SelectedAction { get; set; } + /// 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; } } -/// Request ID of a pending `exit_plan_mode.requested` event and the user's response. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingExitPlanModeRequest +internal sealed class SessionMetadataSnapshotRequest { - /// The unique request ID from the exit_plan_mode.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// 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; } -/// 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 UIRegisterDirectAutoModeSwitchHandlerResult -{ - /// 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 SessionUiRegisterDirectAutoModeSwitchHandlerRequest +internal sealed class SessionMetadataGetClientMetadataRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the handle was active and the registration count was decremented. +/// 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)] -public sealed class UIUnregisterDirectAutoModeSwitchHandlerResult +internal sealed class MetadataUpdateClientMetadataRequest { - /// True if the handle was active and decremented the counter; false if the handle was unknown. - [JsonPropertyName("unregistered")] - public bool Unregistered { get; set; } -} + /// Remove every existing client metadata entry before applying remove and set. Defaults to false. + [JsonPropertyName("clear")] + public bool? Clear { get; set; } -/// Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest -{ - /// Handle previously returned by `registerDirectAutoModeSwitchHandler`. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// 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; -} -/// 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; } + /// 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; } } -/// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. +/// Indicates whether the local session is currently processing a turn or background continuation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource +public sealed class MetadataIsProcessingResult { - /// 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; + /// 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; } } -/// Single content-exclusion rule supplied to `session.permissions.configure`, with paths, match conditions, and source. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRule +internal sealed class SessionMetadataIsProcessingRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Content-exclusion policy supplied to `session.permissions.configure`, with rules, last-updated data, and scope. +/// Current activity flags for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicy +public sealed class SessionActivity { - /// 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 an in-flight operation can currently be aborted. + [JsonPropertyName("abortable")] + public bool Abortable { get; set; } - /// Allowed values for the `PermissionsConfigureAdditionalContentExclusionPolicyScope` enumeration. - [JsonPropertyName("scope")] - public PermissionsConfigureAdditionalContentExclusionPolicyScope Scope { get; set; } + /// Whether the session currently has active work, including running turns or tasks. + [JsonPropertyName("hasActiveWork")] + public bool HasActiveWork { 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsConfig +internal sealed class SessionMetadataActivityRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionRulesSet +/// Token-usage breakdown for the session's current context window. +public sealed class MetadataContextInfoResultContextInfo { - /// Rules that auto-approve matching requests. - [JsonPropertyName("approved")] - public IList Approved { get => field ??= []; set; } - - /// Rules that auto-deny matching requests. - [JsonPropertyName("denied")] - public IList Denied { get => field ??= []; set; } -} + /// 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; } -/// 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 PermissionUrlsConfig -{ - /// Initial list of allowed URL/domain patterns. Patterns may include path components. Ignored when `unrestricted` is true. - [JsonPropertyName("initialAllowed")] - public IList? InitialAllowed { get; set; } + /// Token count at which background compaction starts (configurable percentage of promptTokenLimit). + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { 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; } -} + /// Tokens consumed by user/assistant/tool messages. + [JsonPropertyName("conversationTokens")] + public long ConversationTokens { get; set; } -/// Patch of permission policy fields to apply (omit a field to leave it unchanged). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsConfigureParams -{ - /// 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; } + /// Provider-owned model display label, or the Auto routing label. Presentation only; modelName remains the tokenization identity. + [JsonPropertyName("displayModelName")] + public string? DisplayModelName { 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; } + /// 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; } - /// 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; } + /// Tokens consumed by MCP tool definitions (subset of toolDefinitionsTokens, excludes deferred tools). + [JsonPropertyName("mcpToolsTokens")] + public long McpToolsTokens { 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; } + /// The model used for token counting. + [JsonPropertyName("modelName")] + public string ModelName { get; set; } = string.Empty; - /// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. - [JsonPropertyName("rules")] - public PermissionRulesSet? Rules { get; set; } + /// 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provider whose catalog supplies this context window. Omitted when provider identity is unavailable. + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } - /// 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; } -} + /// Tokens consumed by the system prompt. + [JsonPropertyName("systemTokens")] + public long SystemTokens { get; set; } -/// 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; } -} + /// Tokens consumed by tool definitions sent to the model (excludes deferred tools). + [JsonPropertyName("toolDefinitionsTokens")] + public long ToolDefinitionsTokens { 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; + /// Sum of system, conversation and tool-definition tokens. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } } - -/// Permission-decision request variant to approve only the current permission request. -/// The approve-once variant of . +/// Token breakdown for the session's current context window, or null if uninitialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveOnce : PermissionDecision +public sealed class MetadataContextInfoResult { - /// - [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; } + /// 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; } } -/// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). -/// Polymorphic base type discriminated by kind. +/// Model identifier and token limits used to compute the context-info breakdown. [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 +internal sealed class MetadataContextInfoRequest { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - + /// 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; } -/// 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"; + /// Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from the session. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } - /// Command identifiers covered by this approval. - [JsonPropertyName("commandIdentifiers")] - public required IList CommandIdentifiers { 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; } -/// Session-scoped approval details for read-only filesystem operations. -/// The read variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalRead : PermissionDecisionApproveForSessionApproval -{ - /// - [JsonIgnore] - public override string Kind => "read"; -} + /// 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; } -/// Session-scoped approval details for filesystem write operations. -/// The write variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalWrite : PermissionDecisionApproveForSessionApproval -{ - /// - [JsonIgnore] - public override string Kind => "write"; + /// 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 . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMcp : PermissionDecisionApproveForSessionApproval +/// 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 { - /// - [JsonIgnore] - public override string Kind => "mcp"; - - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Overlapping output reservation plus post-blocking-threshold buffer. + [JsonPropertyName("buffer")] + public long Buffer { get; set; } - /// MCP tool name, or null to cover every tool on the server. - [JsonPropertyName("toolName")] - public string? ToolName { get; set; } -} + /// Custom-instructions tokens (0 when none are configured). + [JsonPropertyName("customInstructions")] + public long CustomInstructions { get; set; } -/// 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 PermissionDecisionApproveForSessionApprovalMcpSampling : PermissionDecisionApproveForSessionApproval -{ - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// Remaining unused window capacity (clamped at 0). + [JsonPropertyName("freeSpace")] + public long FreeSpace { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} + /// MCP tool-definition tokens. + [JsonPropertyName("mcpTools")] + public long McpTools { get; set; } -/// Session-scoped approval details for writes to long-term memory. -/// The memory variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMemory : PermissionDecisionApproveForSessionApproval -{ - /// - [JsonIgnore] - public override string Kind => "memory"; -} + /// Conversation (user/assistant/tool) message tokens. + [JsonPropertyName("messages")] + public long Messages { get; set; } -/// 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 PermissionDecisionApproveForSessionApprovalCustomTool : PermissionDecisionApproveForSessionApproval -{ - /// - [JsonIgnore] - public override string Kind => "custom-tool"; + /// System prompt tokens, excluding custom instructions. + [JsonPropertyName("systemPrompt")] + public long SystemPrompt { get; set; } - /// Custom tool name. - [JsonPropertyName("toolName")] - public required string ToolName { get; set; } + /// Non-MCP tool-definition tokens. + [JsonPropertyName("systemTools")] + public long SystemTools { get; set; } } -/// Session-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 PermissionDecisionApproveForSessionApprovalExtensionManagement : PermissionDecisionApproveForSessionApproval +/// Successful compaction history for the session. +public sealed class MetadataContextAttributionResultContextAttributionCompactions { - /// - [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; } + /// Number of successful compactions in this session. + [JsonPropertyName("count")] + public long Count { get; set; } } -/// Session-scoped workflow approval, optionally narrowed by approval key. -/// The workflow variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalWorkflow : PermissionDecisionApproveForSessionApproval +/// RPC data type for MetadataContextAttributionResultContextAttributionEntry operations. +public sealed class MetadataContextAttributionResultContextAttributionEntry { - /// - [JsonIgnore] - public override string Kind => "workflow"; - - /// 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; } -} + /// 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; } -/// 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"; + /// 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; - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } -} + /// 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; -/// 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"; + /// 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; - /// Names of the sensitive environment variables this approval covers. Values are never persisted. - [JsonPropertyName("environmentVariables")] - public required IList EnvironmentVariables { 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; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// Token count currently in context attributable to this entry. + [JsonPropertyName("tokens")] + public long Tokens { 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 . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSession : PermissionDecision +/// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. +public sealed class MetadataContextAttributionResultContextAttribution { - /// - [JsonIgnore] - public override string Kind => "approve-for-session"; + /// 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; } - /// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approval")] - public PermissionDecisionApproveForSessionApproval? Approval { 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; } - /// URL domain to approve for the rest of the session (URL prompts only). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("domain")] - public string? Domain { get; set; } -} + /// Successful compaction history for the session. + [JsonPropertyName("compactions")] + public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } -/// 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)] -public partial class PermissionDecisionApproveReadOnlyForSession : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "approve-read-only-for-session"; + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } - /// Canonical directories covered by the read-only grant. - [JsonPropertyName("directories")] - public required IList Directories { 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; } -/// Approval to persist for this location. -/// 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 -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// 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; -/// Location-scoped approval details for specific command identifiers. -/// The commands variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalCommands : PermissionDecisionApproveForLocationApproval -{ - /// - [JsonIgnore] - public override string Kind => "commands"; + /// 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; - /// Command identifiers covered by this approval. - [JsonPropertyName("commandIdentifiers")] - public required IList CommandIdentifiers { get; set; } + /// 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; } } -/// Location-scoped approval details for read-only filesystem operations. -/// The read variant of . +/// Per-source attribution breakdown for the session's current context window, or null if uninitialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalRead : PermissionDecisionApproveForLocationApproval +public sealed class MetadataContextAttributionResult { - /// - [JsonIgnore] - public override string Kind => "read"; + /// 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; } } -/// Location-scoped approval details for filesystem write operations. -/// The write variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalWrite : PermissionDecisionApproveForLocationApproval +internal sealed class SessionMetadataGetContextAttributionRequest { - /// - [JsonIgnore] - public override string Kind => "write"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. -/// The mcp variant of . +/// A single large message currently in context. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMcp : PermissionDecisionApproveForLocationApproval +public sealed class ContextHeaviestMessage { - /// - [JsonIgnore] - public override string Kind => "mcp"; + /// Stable identifier for this message within the snapshot. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Human-readable source label, e.g. `tool: bash` or `skill: tmux`. Presentation-only. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; - /// MCP tool name, or null to cover every tool on the server. - [JsonPropertyName("toolName")] - public string? ToolName { 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; } } -/// Location-scoped approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// The heaviest individual messages in the session's context window, most-expensive first. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : PermissionDecisionApproveForLocationApproval +public sealed class MetadataContextHeaviestMessagesResult { - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// Heaviest messages, most-expensive first. + [JsonPropertyName("messages")] + public IList Messages { get => field ??= []; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { 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; } } -/// Location-scoped approval details for writes to long-term memory. -/// The memory variant of . +/// Parameters for the heaviest-messages query. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMemory : PermissionDecisionApproveForLocationApproval +internal sealed class MetadataContextHeaviestMessagesRequest { - /// - [JsonIgnore] - public override string Kind => "memory"; + /// 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; } -/// Location-scoped approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// 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 partial class PermissionDecisionApproveForLocationApprovalCustomTool : PermissionDecisionApproveForLocationApproval +public sealed class MetadataRecordContextChangeResult { - /// - [JsonIgnore] - public override string Kind => "custom-tool"; - - /// Custom tool name. - [JsonPropertyName("toolName")] - public required string ToolName { get; set; } } -/// Location-scoped approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Updated working-directory/git context to record on the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalExtensionManagement : PermissionDecisionApproveForLocationApproval +internal sealed class MetadataRecordContextChangeRequest { - /// - [JsonIgnore] - public override string Kind => "extension-management"; + /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. + [JsonPropertyName("context")] + public SessionWorkingDirectoryContext Context { get => field ??= new(); set; } - /// Optional operation identifier; when omitted, the approval covers all extension management operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operation")] - public string? Operation { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-scoped workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// 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 partial class PermissionDecisionApproveForLocationApprovalWorkflow : PermissionDecisionApproveForLocationApproval +public sealed class MetadataSetWorkingDirectoryResult { - /// - [JsonIgnore] - public override string Kind => "workflow"; - - /// 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 after the update. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Location-scoped approval details for an extension's permission-gated capability access, keyed by extension name. -/// The extension-permission-access variant of . +/// 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)] -public partial class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess : PermissionDecisionApproveForLocationApproval +internal sealed class MetadataSetWorkingDirectoryRequest { - /// - [JsonIgnore] - public override string Kind => "extension-permission-access"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// 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; } -/// 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 . +/// 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 partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAccess : PermissionDecisionApproveForLocationApproval +public sealed class MetadataRecomputeContextTokensResult { - /// - [JsonIgnore] - public override string Kind => "extension-env-access"; + /// Tokens contributed by user/assistant/tool messages (excludes system/developer prompts). + [JsonPropertyName("messagesTokenCount")] + public long MessagesTokenCount { get; set; } - /// Names of the sensitive environment variables this approval covers. Values are never persisted. - [JsonPropertyName("environmentVariables")] - public required IList EnvironmentVariables { get; set; } + /// Tokens contributed by system/developer prompt snapshots. + [JsonPropertyName("systemTokenCount")] + public long SystemTokenCount { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// Sum of tokens across chat-context and system-context messages currently held by the session. + [JsonPropertyName("totalTokens")] + public long TotalTokens { 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 . +/// Model identifier to use when re-tokenizing the session's existing messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocation : PermissionDecision +internal sealed class MetadataRecomputeContextTokensRequest { - /// - [JsonIgnore] - public override string Kind => "approve-for-location"; - - /// Approval to persist for this location. - [JsonPropertyName("approval")] - public required PermissionDecisionApproveForLocationApproval Approval { get; set; } + /// 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; - /// Location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public required string LocationKey { get; set; } + /// 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 . +/// Availability of built-in job tools surfaced to boundary consumers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovePermanently : PermissionDecision +public sealed class SessionSettingsBuiltInToolAvailabilitySnapshot { - /// - [JsonIgnore] - public override string Kind => "approve-permanently"; + /// Whether the create-pull-request tool is available. + [JsonPropertyName("createPullRequest")] + public bool? CreatePullRequest { get; set; } - /// URL domain to approve permanently. - [JsonPropertyName("domain")] - public required string Domain { get; set; } + /// Whether the report-progress tool is available. + [JsonPropertyName("reportProgress")] + public bool? ReportProgress { get; set; } } -/// Permission-decision request variant to reject a pending permission request, with optional feedback. -/// The reject variant of . +/// Redacted job settings for a session. The job nonce is excluded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionReject : PermissionDecision +public sealed class SessionSettingsJobSnapshot { - /// - [JsonIgnore] - public override string Kind => "reject"; + /// Availability of job-specific built-in tools. + [JsonPropertyName("builtInToolAvailability")] + public SessionSettingsBuiltInToolAvailabilitySnapshot? BuiltInToolAvailability { get; set; } - /// Optional feedback explaining the rejection. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } -} + /// GitHub Actions event type for the job. + [JsonPropertyName("eventType")] + public string? EventType { get; set; } -/// 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 partial class PermissionDecisionUserNotAvailable : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "user-not-available"; + /// Whether this is the workflow's trigger job. + [JsonPropertyName("isTriggerJob")] + public bool? IsTriggerJob { get; set; } } -/// Permission-decision variant indicating the request was approved. -/// The approved variant of . +/// Redacted model routing settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproved : PermissionDecision +public sealed class SessionSettingsModelSnapshot { - /// - [JsonIgnore] - public override string Kind => "approved"; -} + /// Agent service callback URL for job and progress updates. + [JsonPropertyName("callbackUrl")] + public string? CallbackUrl { get; set; } -/// 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)] -public partial class PermissionDecisionApprovedForSession : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "approved-for-session"; + /// Default reasoning effort for the selected model. + [JsonPropertyName("defaultReasoningEffort")] + public string? DefaultReasoningEffort { get; set; } - /// The approval to add as a session-scoped rule. - [JsonPropertyName("approval")] - public required UserToolSessionApproval Approval { 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; } } -/// Permission-decision variant indicating approval was persisted for a project location, with approval details and location key. -/// The approved-for-location variant of . +/// Online-evaluation settings safe to expose across the SDK boundary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovedForLocation : PermissionDecision +public sealed class SessionSettingsOnlineEvaluationSnapshot { - /// - [JsonIgnore] - public override string Kind => "approved-for-location"; - - /// The approval to persist for this location. - [JsonPropertyName("approval")] - public required UserToolSessionApproval Approval { get; set; } + /// Whether online evaluation is disabled. + [JsonPropertyName("disableOnlineEvaluation")] + public bool? DisableOnlineEvaluation { get; set; } - /// The location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public required string LocationKey { get; set; } + /// Whether online-evaluation output-file generation is enabled. + [JsonPropertyName("enableOnlineEvaluationOutputFile")] + public bool? EnableOnlineEvaluationOutputFile { get; set; } } -/// Permission-decision variant indicating the request was cancelled before use, with an optional reason. -/// The cancelled variant of . +/// Redacted repository and GitHub host settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionCancelled : PermissionDecision +public sealed class SessionSettingsRepoSnapshot { - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// Checked-out repository branch. + [JsonPropertyName("branch")] + public string? Branch { get; set; } - /// Optional explanation of why the request was cancelled. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } -} + /// Checked-out commit SHA. + [JsonPropertyName("commit")] + public string? Commit { get; 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"; + /// GitHub server host name. + [JsonPropertyName("host")] + public string? Host { get; set; } - /// Rules that denied the request. - [JsonPropertyName("rules")] - public required IList Rules { get; set; } -} + /// Protocol used to access the GitHub host. + [JsonPropertyName("hostProtocol")] + public string? HostProtocol { 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 -{ - /// - [JsonIgnore] - public override string Kind => "denied-no-approval-rule-and-could-not-request-from-user"; -} + /// GitHub repository database ID. + [JsonPropertyName("id")] + public double? Id { 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"; + /// Repository name. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Optional feedback from the user explaining the denial. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } + /// GitHub repository owner database ID. + [JsonPropertyName("ownerId")] + public double? OwnerId { get; set; } - /// Whether to force-reject the current agent turn. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("forceReject")] - public bool? ForceReject { get; set; } -} + /// Repository owner login. + [JsonPropertyName("ownerName")] + public string? OwnerName { get; set; } -/// 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)] -public partial class PermissionDecisionDeniedByContentExclusionPolicy : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "denied-by-content-exclusion-policy"; + /// Number of commits in the pull request. + [JsonPropertyName("prCommitCount")] + public double? PrCommitCount { get; set; } - /// Human-readable explanation of why the path was excluded. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Whether the repository is writable. + [JsonPropertyName("readWrite")] + public bool? ReadWrite { get; set; } - /// File path that triggered the exclusion. - [JsonPropertyName("path")] - public required string Path { 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; } } -/// Permission-decision variant indicating denial by a permission request hook, with optional message and interrupt flag. -/// The denied-by-permission-request-hook variant of . +/// Redacted validation and memory-tool settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedByPermissionRequestHook : PermissionDecision +public sealed class SessionSettingsValidationSnapshot { - /// - [JsonIgnore] - public override string Kind => "denied-by-permission-request-hook"; + /// Whether advisory validation is enabled. + [JsonPropertyName("advisoryEnabled")] + public bool? AdvisoryEnabled { get; set; } - /// Whether to interrupt the current agent turn. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("interrupt")] - public bool? Interrupt { get; set; } + /// Whether CodeQL validation is enabled. + [JsonPropertyName("codeqlEnabled")] + public bool? CodeqlEnabled { get; set; } - /// Optional message from the hook explaining the denial. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } -} + /// Whether code-review validation is enabled. + [JsonPropertyName("codeReviewEnabled")] + public bool? CodeReviewEnabled { 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; } + /// Model used for code-review validation. + [JsonPropertyName("codeReviewModel")] + public string? CodeReviewModel { get; set; } - /// Request ID of the pending permission request. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Dependabot validation timeout budget in seconds. + [JsonPropertyName("dependabotTimeout")] + public double? DependabotTimeout { get; set; } - /// The client's response to the pending permission prompt. - [JsonPropertyName("result")] - public PermissionDecision Result { get => field ??= new(); set; } + /// Whether the memory-store tool is enabled. + [JsonPropertyName("memoryStoreEnabled")] + public bool? MemoryStoreEnabled { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Pending permission prompt reconstructed from event history, with request ID and user-facing prompt details. +/// 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)] -public sealed class PendingPermissionRequest +internal sealed class SessionSettingsSnapshot { - /// Permission-recovery episode that authorized this request to surface for interactive attention. - [JsonPropertyName("recoveryEpisodeId")] - public string? RecoveryEpisodeId { get; set; } + /// Name of the SDK client that created the session. + [JsonPropertyName("clientName")] + public string? ClientName { 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!; + /// Redacted job settings. + [JsonPropertyName("job")] + public SessionSettingsJobSnapshot Job { get => field ??= new(); set; } - /// Unique identifier for the pending permission request. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; -} + /// Redacted model routing settings. + [JsonPropertyName("model")] + public SessionSettingsModelSnapshot Model { get => field ??= new(); 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; } + /// 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; } } -/// 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 SessionSettingsSnapshotRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Result of evaluating a Rust-owned settings predicate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetApproveAllResult +internal sealed class SessionSettingsEvaluatePredicateResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the named settings predicate evaluated to enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } } -/// Allow-all toggle for tool permission requests, with an optional telemetry source. +/// Named Rust-owned settings predicate to evaluate for this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetApproveAllRequest +internal sealed class SessionSettingsEvaluatePredicateRequest { - /// Whether to auto-approve all tool permission requests. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// 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; - /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. - [JsonPropertyName("source")] - public PermissionsSetApproveAllSource? Source { get; set; } + /// Tool name for tool-scoped predicates such as trivial-change handling. + [JsonPropertyName("toolName")] + public string? ToolName { get; set; } } -/// Indicates whether the requested permission mode was applied and reports the authoritative post-mutation mode. +/// Content-exclusion decision for one requested path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetModeResult +public sealed class ContentExclusionPathCheck { - /// Authoritative permission mode after the mutation. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } + /// Whether the session's complete content-exclusion policy excludes the path. + [JsonPropertyName("excluded")] + public bool Excluded { get; set; } - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// The path supplied by the caller. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// Permission mode to apply for the session. +/// Batch content-exclusion result. Callers must fail closed when policy evaluation is unavailable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetModeRequest +public sealed class ContentExclusionCheckPathsResult { - /// 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; + /// 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; } - /// 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; } + /// Per-path decisions in request order. Empty when available is false. + [JsonPropertyName("checks")] + public IList Checks { get => field ??= []; set; } } -/// Current permission mode. +/// Local file system absolute paths within the session working directory to check against its content-exclusion policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsGetModeResult +internal sealed class ContentExclusionCheckPathsRequest { - /// Current permission mode. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } -} + /// 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; } -/// No parameters. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsGetModeRequest -{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Identifier of the spawned shell process, usable with shell.kill while the process is running. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsModifyRulesResult +public sealed class ShellExecResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Identifier usable with shell.kill while the process is running. + [JsonPropertyName("processId")] + public string ProcessId { get; set; } = string.Empty; } -/// Scope and add/remove instructions for modifying session- or location-scoped permission rules. +/// Shell command to run, with optional working directory and timeout in milliseconds. Spawn failures return an RPC error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsModifyRulesParams +internal sealed class ShellExecRequest { - /// 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; } + /// Shell command to execute. + [JsonPropertyName("command")] + public string Command { get; set; } = string.Empty; - /// 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; } + /// 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; } } -/// Indicates whether the operation succeeded. +/// Indicates whether the signal was delivered; false if the process was unknown or already exited. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetRequiredResult +public sealed class ShellKillResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the signal was sent successfully. + [JsonPropertyName("killed")] + public bool Killed { get; set; } } -/// Toggles whether permission prompts should be bridged into session events for this client. +/// Identifier of a process previously returned by "shell.exec" and the signal to send. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetRequiredRequest +internal sealed class ShellKillRequest { - /// 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; } + /// 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; } } -/// Indicates whether the operation succeeded. +/// Result of a user-requested shell command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsResetSessionApprovalsResult +public sealed class UserRequestedShellCommandResult { - /// Whether the operation succeeded. + /// Error output when the execution failed. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Process exit code, when available. + [JsonPropertyName("exitCode")] + public long? ExitCode { 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; } -/// Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. +/// User-requested shell command and cancellation handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsResetSessionApprovalsRequest +internal sealed class ShellExecuteUserRequestedRequest { - /// Whether location-scoped approvals and exact session-approved paths are cleared too. Defaults to `true`. - [JsonPropertyName("includeLocation")] - public bool? IncludeLocation { get; set; } + /// 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; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Cancellation result for a user-requested shell command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsNotifyPromptShownResult +public sealed class CancelUserRequestedShellCommandResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether an in-flight execution was found and signalled to cancel. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// Notification payload describing the permission prompt that the client just rendered. +/// User-requested shell execution cancellation handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPromptShownNotification +internal sealed class ShellCancelUserRequestedRequest { - /// 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; + /// 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; } -/// Snapshot of the session's recursive directory grants, exact session-approved paths, and primary working directory. +/// Post-compaction context window usage breakdown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsList +public sealed class HistoryCompactContextWindow { - /// All directories currently allowed for tool access on this session. - [JsonPropertyName("directories")] - public IList Directories { get => field ??= []; set; } + /// Token count from non-system messages (user, assistant, tool). + [JsonPropertyName("conversationTokens")] + public long? ConversationTokens { get; set; } - /// The primary working directory for this session. - [JsonPropertyName("primary")] - public string Primary { get; set; } = string.Empty; + /// Current total tokens in the context window (system + conversation + tool definitions). + [JsonPropertyName("currentTokens")] + public long CurrentTokens { get; set; } - /// Exact paths approved for this session without recursively allowing their descendants. - [JsonPropertyName("sessionApprovedPaths")] - public IList? SessionApprovedPaths { get; set; } -} + /// Current number of messages in the conversation. + [JsonPropertyName("messagesLength")] + public long MessagesLength { get; set; } -/// No parameters; returns the session's recursive directory grants and exact session-approved paths. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsPathsListRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Token count from system message(s). + [JsonPropertyName("systemTokens")] + public long? SystemTokens { get; set; } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsPathsAddResult -{ - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { 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; } } -/// Directory path to add to the session's allowed directories. +/// 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 PermissionPathsAddParams +public sealed class HistoryCompactResult { - /// 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; + /// 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; } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsPathsUpdatePrimaryResult -{ - /// Whether the operation succeeded. + /// Whether compaction completed successfully. [JsonPropertyName("success")] public bool Success { get; set; } -} - -/// Directory path to set as the session's new primary working directory. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsUpdatePrimaryParams -{ - /// 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; -} + /// Summary text produced by compaction. Omitted when compaction did not produce a summary (e.g. failure path). + [JsonPropertyName("summaryContent")] + public string? SummaryContent { get; set; } -/// Indicates whether the supplied path is within the session's allowed directories. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsAllowedCheckResult -{ - /// Whether the path is within the session's allowed directories. - [JsonPropertyName("allowed")] - public bool Allowed { get; set; } + /// Number of tokens freed by compaction. + [JsonPropertyName("tokensRemoved")] + public long TokensRemoved { get; set; } } -/// Path to evaluate against the session's allowed directories. +/// RPC data type for SessionHistoryCompact operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsAllowedCheckParams +public sealed class SessionHistoryCompactRequest { - /// Path to check against the session's allowed directories. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// 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; } -/// Indicates whether the supplied path is within the session's workspace directory. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsWorkspaceCheckResult -{ - /// Whether the path is within the session workspace directory. - [JsonPropertyName("allowed")] - public bool Allowed { 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; } } -/// Path to evaluate against the session's workspace (primary) directory. +/// RPC data type for SessionHistoryCompactRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsWorkspaceCheckParams +internal sealed class SessionHistoryCompactRequestWithSession { - /// Path to check against the session workspace directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// 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; -} - -/// Resolved location-permissions key and type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionLocationResolveResult -{ - /// 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; } -} -/// Working directory to resolve into a location-permissions key. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationResolveParams -{ - /// 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; } - /// Working directory whose permission location should be resolved. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// 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; } } -/// Summary of persisted location permissions applied to the session. +/// Number of events that were removed by the truncation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionLocationApplyResult +public sealed class HistoryTruncateResult { - /// Number of persisted allowed directories added to the live path manager. - [JsonPropertyName("appliedDirectoryCount")] - public long AppliedDirectoryCount { get; set; } - - /// Number of location-scoped rules added to the live permission service. - [JsonPropertyName("appliedRuleCount")] - public long AppliedRuleCount { get; set; } - - /// Location-scoped rules applied to the live permission service. - [JsonPropertyName("appliedRules")] - public IList AppliedRules { get => field ??= []; set; } - - /// Whether a different location was applied since the previous apply call. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// Failure detail when checkpointCleanupFailed is true. + [JsonPropertyName("checkpointCleanupError")] + public string? CheckpointCleanupError { get; set; } - /// Location key used in the location-permissions store. - [JsonPropertyName("locationKey")] - public string LocationKey { 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; } - /// Whether the location is a git repo or directory. - [JsonPropertyName("locationType")] - public PermissionLocationType LocationType { get; set; } + /// Number of events that were removed. + [JsonPropertyName("eventsRemoved")] + public long EventsRemoved { get; set; } } -/// Working directory to load persisted location permissions for. +/// Identifier of the event to truncate to; this event and all later events are removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationApplyParams +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; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Working directory whose persisted location permissions should be applied. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// A root user turn that the session can rewind to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsLocationsAddToolApprovalResult +public sealed class HistoryRewindPoint { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } -} + /// Whether at least one file in this turn or a later turn can be restored. + [JsonPropertyName("canRestoreFiles")] + public bool CanRestoreFiles { get; set; } -/// 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(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")] - public virtual string Kind { get; set; } = string.Empty; -} + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; + + /// Number of unique files in this turn and all later turns that have captured changes. + [JsonPropertyName("fileCount")] + public long FileCount { get; set; } + /// Whether this turn was an automatically injected autopilot continuation. + [JsonPropertyName("isAutopilotContinuation")] + public bool IsAutopilotContinuation { get; 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"; + /// Lines added by this turn's captured file changes. + [JsonPropertyName("linesAdded")] + public long LinesAdded { get; set; } - /// Command identifiers covered by this approval. - [JsonPropertyName("commandIdentifiers")] - public required IList CommandIdentifiers { get; set; } + /// Lines removed by this turn's captured file changes. + [JsonPropertyName("linesRemoved")] + public long LinesRemoved { get; set; } + + /// ISO timestamp of the user turn. + [JsonPropertyName("timestamp")] + public string Timestamp { get; set; } = string.Empty; + + /// Whether this turn itself captured any file changes. + [JsonPropertyName("turnChangedFiles")] + public bool TurnChangedFiles { get; set; } + + /// User-visible message text for the turn. + [JsonPropertyName("userMessage")] + public string UserMessage { get; set; } = string.Empty; } -/// Location-persisted tool approval details for read-only filesystem operations. -/// The read variant of . +/// Rewind points and file-change-tracking availability for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsRead : PermissionsLocationsAddToolApprovalDetails +public sealed class HistoryListRewindPointsResult { - /// - [JsonIgnore] - public override string Kind => "read"; + /// Whether this session captured file changes from its first turn. + [JsonPropertyName("fileChangeTrackingEnabled")] + public bool FileChangeTrackingEnabled { get; set; } + + /// Root user turns in chronological order. Empty when `unavailableReason` is set. + [JsonPropertyName("points")] + public IList Points { get => field ??= []; 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; } } -/// Location-persisted tool approval details for filesystem write operations. -/// The write variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsWrite : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionHistoryListRewindPointsRequest { - /// - [JsonIgnore] - public override string Kind => "write"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-persisted tool approval details for an MCP server tool, or all tools when `toolName` is null. -/// The mcp variant of . +/// A file that a conversation-and-files rewind would restore. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMcp : PermissionsLocationsAddToolApprovalDetails +public sealed class HistoryRewindFilePreview { - /// - [JsonIgnore] - public override string Kind => "mcp"; + /// Aggregate change made across the discarded turns. + [JsonPropertyName("changeType")] + public HistoryRewindChangeType ChangeType { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Lines added across the discarded turns. + [JsonPropertyName("linesAdded")] + public long LinesAdded { get; set; } - /// MCP tool name, or null to cover every tool on the server. - [JsonPropertyName("toolName")] - public string? ToolName { get; set; } + /// Lines removed across the discarded turns. + [JsonPropertyName("linesRemoved")] + public long LinesRemoved { get; set; } + + /// Absolute path of the captured file. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// Location-persisted tool approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Files and aggregate changes for a prospective rewind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMcpSampling : PermissionsLocationsAddToolApprovalDetails +public sealed class HistoryPreviewRewindResult { - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// 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; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } -} + /// Number of unique files in the preview. + [JsonPropertyName("fileCount")] + public long FileCount { get; set; } -/// Location-persisted tool approval details for writes to long-term memory. -/// The memory variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMemory : PermissionsLocationsAddToolApprovalDetails -{ - /// - [JsonIgnore] - public override string Kind => "memory"; + /// 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; } } -/// Location-persisted tool approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// Event boundary to preview for conversation-and-files rewind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsCustomTool : PermissionsLocationsAddToolApprovalDetails +internal sealed class HistoryPreviewRewindRequest { - /// - [JsonIgnore] - public override string Kind => "custom-tool"; + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { 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; } -/// Location-persisted tool approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// A captured file that rewind intentionally left unchanged. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManagement : PermissionsLocationsAddToolApprovalDetails +public sealed class HistorySkippedFileRestore { - /// - [JsonIgnore] - public override string Kind => "extension-management"; + /// Absolute path of the skipped file. + [JsonPropertyName("path")] + public string Path { 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; } + /// Reason the file was not restored. + [JsonPropertyName("reason")] + public HistoryFileRestoreSkipReason Reason { get; set; } } -/// Location-persisted workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// Structured outcome of a rewind request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsWorkflow : PermissionsLocationsAddToolApprovalDetails +public sealed class HistoryRewindResult { - /// - [JsonIgnore] - public override string Kind => "workflow"; + /// 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; } - /// 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; } -} + /// 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; } -/// 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 PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess : PermissionsLocationsAddToolApprovalDetails -{ - /// - [JsonIgnore] - public override string Kind => "extension-permission-access"; + /// 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; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { 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; } + + /// 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; } } -/// 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 . +/// Boundary and mode for rewinding session history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess : PermissionsLocationsAddToolApprovalDetails +internal sealed class HistoryRewindRequest { - /// - [JsonIgnore] - public override string Kind => "extension-env-access"; + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { 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; } + /// Whether to rewind only conversation history or also restore captured files. + [JsonPropertyName("mode")] + public HistoryRewindMode Mode { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-scoped tool approval to persist. +/// Indicates whether an in-progress background compaction was cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationAddToolApprovalParams +public sealed class HistoryCancelBackgroundCompactionResult { - /// Tool approval to persist and apply. - [JsonPropertyName("approval")] - public PermissionsLocationsAddToolApprovalDetails Approval { get => field ??= new(); set; } - - /// Location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public string LocationKey { get; set; } = string.Empty; + /// 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 SessionHistoryCancelBackgroundCompactionRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Folder trust check result. +/// Indicates whether an in-progress manual compaction was aborted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class FolderTrustCheckResult +public sealed class HistoryAbortManualCompactionResult { - /// Whether the folder is trusted. - [JsonPropertyName("trusted")] - public bool Trusted { 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; } } -/// Folder path to check for trust. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FolderTrustCheckParams +internal sealed class SessionHistoryAbortManualCompactionRequest { - /// Folder path to check. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Markdown summary of the conversation context (empty when not available). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsFolderTrustAddTrustedResult +public sealed class HistorySummarizeForHandoffResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// 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; } -/// Folder path to add to trusted folders. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FolderTrustAddParams +internal sealed class SessionHistorySummarizeForHandoffRequest { - /// 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; } -/// Indicates whether the operation succeeded. +/// 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 PermissionsUrlsSetUnrestrictedModeResult +public sealed class HistoryClearContextResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { 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; } } -/// Whether the URL-permission policy should run in unrestricted mode. +/// Parameters for clearing the conversation and seeding the window that replaces it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionUrlsSetUnrestrictedModeParams +internal sealed class HistoryClearContextRequest { - /// 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; } + /// 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; } -/// The repository the remote session targets. +/// 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 MetadataSnapshotRemoteMetadataRepository +public sealed class QueuePendingItems { - /// The branch the remote session is operating on. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; + /// 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; } - /// The GitHub repository name (without owner). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Human-readable text to display for this queue entry in the UI. + [JsonPropertyName("displayText")] + public string DisplayText { get; set; } = string.Empty; - /// The GitHub owner (user or organization) of the target repository. - [JsonPropertyName("owner")] - public string Owner { 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; + + /// Whether this item is a queued user message or a queued slash command / model change. + [JsonPropertyName("kind")] + public QueuePendingItemsKind Kind { 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; } + + /// 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; } } -/// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. +/// Snapshot of the session's pending queued items and immediate-steering messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSnapshotRemoteMetadata +public sealed class QueuePendingItemsResult { - /// The pull request number the remote session is associated with, if any. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; 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; } - /// The repository the remote session targets. - [JsonPropertyName("repository")] - public MetadataSnapshotRemoteMetadataRepository Repository { 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; } - /// 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; } + /// Display text for messages currently in the immediate steering queue (interjections sent during a running turn). + [JsonPropertyName("steeringMessages")] + public IList SteeringMessages { get => field ??= []; set; } - /// Whether the remote task originated from Copilot Coding Agent (cca) or a CLI `--remote` invocation. - [JsonPropertyName("taskType")] - public MetadataSnapshotRemoteMetadataTaskType? TaskType { get; 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; } } -/// Public-facing projection of workspace metadata for SDK / TUI consumers. -public sealed class SessionMetadataSnapshotWorkspace +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionQueuePendingItemsRequest { - /// Branch checked out at session start, if any. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// ISO 8601 timestamp when the workspace was created. - [JsonPropertyName("created_at")] - public DateTimeOffset? CreatedAt { get; set; } +/// 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; } - /// Current working directory at session start. - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Insertion orders for queued items, aligned with `items`. + [JsonPropertyName("itemOrders")] + public IList? ItemOrders { get; set; } - /// Resolved git root for cwd, if any. - [JsonPropertyName("git_root")] - public string? GitRoot { get; set; } + /// User-facing pending items in FIFO order. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } - /// Repository host type, if known. - [JsonPropertyName("host_type")] - public WorkspaceSummaryHostType? HostType { get; set; } + /// Insertion orders for immediate steering messages, aligned with `steeringMessages`. + [JsonPropertyName("steeringMessageOrders")] + public IList? SteeringMessageOrders { 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; + /// Immediate steering messages waiting for an active turn. + [JsonPropertyName("steeringMessages")] + public IList SteeringMessages { get => field ??= []; set; } +} - /// Display name for the session, if set. - [JsonPropertyName("name")] - public string? Name { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionQueueSnapshotRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Repository identifier in 'owner/repo' or 'org/project/repo' format, if any. - [JsonPropertyName("repository")] - public string? Repository { get; set; } +/// Result of moving a queued item. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueMoveItemResult +{ + /// True when the item changed position; false when it was already at the requested position. + [JsonPropertyName("changed")] + public bool Changed { get; set; } +} - /// ISO 8601 timestamp when the workspace was last updated. - [JsonPropertyName("updated_at")] - public DateTimeOffset? UpdatedAt { 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; - /// Whether the display name was explicitly set by the user. - [JsonPropertyName("user_named")] - public bool? UserNamed { 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; } } -/// Point-in-time snapshot of slow-changing session identifier and state fields. +/// Result of inserting a queued message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMetadataSnapshot +public sealed class QueueInsertAtResult { - /// 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; } + /// Fresh stable opaque id assigned to the inserted item. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; +} - /// User-provided name supplied at session construction (via `--name`), if any. Immutable after construction. - [JsonPropertyName("initialName")] - public string? InitialName { get; set; } +/// Serializable message fields accepted by queue.insertAt. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueInsertMessage +{ + /// Optional explicit agent mode. When omitted, the session's current mode is assigned. + [JsonPropertyName("agentMode")] + public SendAgentMode? AgentMode { 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; } + /// Optional attachments for the message. + [JsonPropertyName("attachments")] + public IList? Attachments { 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 the message is billable. + [JsonPropertyName("billable")] + public bool? Billable { 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; } + /// Accepted for internal SendOptions compatibility but ignored; delivery is derived from current session activity. + [JsonPropertyName("delivery")] + public string? Delivery { get; set; } - /// Currently selected model identifier, if any. - [JsonPropertyName("selectedModel")] - public string? SelectedModel { get; set; } + /// Optional user-facing display text. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } - /// The unique identifier of the session. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Accepted for SendOptions compatibility but ignored; inserted items always use queued delivery semantics. + [JsonPropertyName("mode")] + public SendMode? Mode { get; set; } - /// Current session limits, or null when no limits are active. - [JsonPropertyName("sessionLimits")] - public SessionLimitsConfig? SessionLimits { get; set; } + /// Accepted for SendOptions compatibility but ignored; the requested public position controls placement. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } - /// ISO 8601 timestamp of when the session started. - [JsonPropertyName("startTime")] - public DateTimeOffset StartTime { get; set; } + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { 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; } + /// Per-turn request headers. + [JsonPropertyName("requestHeaders")] + public IDictionary? RequestHeaders { get; set; } - /// Absolute path to the session's current working directory. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// Required tool name for the turn, when any. + [JsonPropertyName("requiredTool")] + public string? RequiredTool { get; set; } - /// 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; } + /// 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; } - /// 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; } + /// Accepted for SendOptions compatibility but ignored; insertion scheduling is controlled by the queue drain state. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// Identifies the target session. +/// Parameters for inserting a queued message at a public visible position. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataSnapshotRequest +internal sealed class QueueInsertAtRequest { + /// 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; } -/// Identifies the target session. +/// Result of removing a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataGetClientMetadataRequest +public sealed class QueueRemoveAtResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// True when the addressed item was removed. + [JsonPropertyName("removed")] + public bool Removed { 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. +/// Parameters for removing a queued item by stable id. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataUpdateClientMetadataRequest +internal sealed class QueueRemoveAtRequest { - /// 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; } + /// 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; - - /// 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 local session is currently processing a turn or background continuation. +/// Result of editing a queued message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataIsProcessingResult +public sealed class QueueUpdateTextResult { - /// 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; } + /// True when the stored text changed. + [JsonPropertyName("updated")] + public bool Updated { get; set; } } -/// Identifies the target session. +/// Parameters for editing a single queued message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataIsProcessingRequest +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; } -/// Current activity flags for the session. +/// Result of withdrawing a user message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionActivity +public sealed class QueueWithdrawMessageResult { - /// Whether an in-flight operation can currently be aborted. - [JsonPropertyName("abortable")] - public bool Abortable { 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; } - /// Whether the session currently has active work, including running turns or tasks. - [JsonPropertyName("hasActiveWork")] - public bool HasActiveWork { get; set; } + /// True when the message left the queue or, for a running turn, history. + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// Identifies the target session. +/// 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 SessionMetadataActivityRequest +internal sealed class QueueWithdrawMessageRequest { + /// 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; } -/// Token-usage breakdown for the session's current context window. -public sealed class MetadataContextInfoResultContextInfo +/// Append to one pending steering message without changing its identity or delivery position. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueAppendSteeringRequest { - /// 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; } + /// Mode captured at submission. Only steering messages in the same mode may be combined. + [JsonPropertyName("agentMode")] + public SendAgentMode AgentMode { get; set; } - /// The model used for token counting. - [JsonPropertyName("modelName")] - public string ModelName { get; set; } = string.Empty; + /// Attachments to add after the message's existing attachments. An empty list preserves the existing attachments. + [JsonPropertyName("attachments")] + public IList Attachments { get => field ??= []; set; } - /// 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; } + /// Display text to append to the existing preview after a blank line. + [JsonPropertyName("displayPrompt")] + public string DisplayPrompt { get; set; } = string.Empty; - /// Provider whose catalog supplies this context window. Omitted when provider identity is unavailable. - [JsonPropertyName("provider")] - public ModelProviderRef? Provider { 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; - /// Tokens consumed by the system prompt. - [JsonPropertyName("systemTokens")] - public long SystemTokens { 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; - /// Tokens consumed by tool definitions sent to the model (excludes deferred tools). - [JsonPropertyName("toolDefinitionsTokens")] - public long ToolDefinitionsTokens { get; set; } + /// Text to append after a blank line. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// Sum of system, conversation and tool-definition tokens. - [JsonPropertyName("totalTokens")] - public long TotalTokens { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Token breakdown for the session's current context window, or null if uninitialized. +/// Result of duplicating a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextInfoResult +public sealed class QueueDuplicateAtResult { - /// 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; } + /// Fresh stable opaque id assigned to the duplicate. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; } -/// Model identifier and token limits used to compute the context-info breakdown. +/// Parameters for duplicating a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataContextInfoRequest +internal sealed class QueueDuplicateAtRequest { - /// 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; } + /// Stable opaque ID of the queued item to duplicate. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// 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 +/// 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 { - /// 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; } + /// Whether queued-lane draining should be paused. + [JsonPropertyName("paused")] + public bool Paused { get; set; } - /// Non-MCP tool-definition tokens. - [JsonPropertyName("systemTools")] - public long SystemTools { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Successful compaction history for the session. -public sealed class MetadataContextAttributionResultContextAttributionCompactions +/// Result of trying to steer a queued message into a live turn. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueSendNowResult { - /// Number of successful compactions in this session. - [JsonPropertyName("count")] - public long Count { 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; } } -/// RPC data type for MetadataContextAttributionResultContextAttributionEntry operations. -public sealed class MetadataContextAttributionResultContextAttributionEntry +/// Parameters for steering a queued message into a live turn. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueSendNowRequest { - /// 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`. + /// Stable opaque ID of the queued item to steer into the live turn. [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; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { 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; } +/// 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; } +} - /// Token count currently in context attributable to this entry. - [JsonPropertyName("tokens")] - public long Tokens { get; set; } +/// 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; } -/// 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 a deferred-idle drain should run. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueBeginDeferredIdleDrainResult { - /// 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; } + /// True when the host should run finishDeferredIdleDrain asynchronously. + [JsonPropertyName("shouldDrain")] + public bool ShouldDrain { 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; } +/// 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; } - /// Successful compaction history for the session. - [JsonPropertyName("compactions")] - public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. - [JsonPropertyName("compactionThreshold")] - public long CompactionThreshold { get; 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; } - /// 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; } + /// One of none, processQueue, or emitSessionIdle. + [JsonPropertyName("action")] + public string Action { get; set; } = string.Empty; +} - /// 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; } +/// 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; } - /// 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; + /// Whether native queued work remains. + [JsonPropertyName("hasPending")] + public bool HasPending { 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; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { 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; } +/// Inputs for marking session.idle deferred in native state. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueDeferSessionIdleRequest +{ + /// Whether the deferred idle was caused by an aborted foreground turn. + [JsonPropertyName("aborted")] + public bool Aborted { 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Per-source attribution breakdown for the session's current context window, or null if uninitialized. +/// Indicates whether a user-facing pending item was removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextAttributionResult +public sealed class QueueRemoveMostRecentResult { - /// 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; } + /// 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; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataGetContextAttributionRequest +internal sealed class SessionQueueRemoveMostRecentRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A single large message currently in context. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContextHeaviestMessage +internal sealed class SessionQueueClearRequest { - /// Stable identifier for this message within the snapshot. - [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; +/// Internal filter for consuming queued system notifications. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueConsumeSystemNotificationsRequest +{ + /// Opaque runtime-owned filter object. + [JsonPropertyName("filter")] + public JsonElement Filter { get; set; } - /// Token count currently in context for this individual message. - [JsonPropertyName("tokens")] - public long Tokens { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The heaviest individual messages in the session's context window, most-expensive first. +/// Result of enqueueing the resume-pending wake item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextHeaviestMessagesResult +internal sealed class QueueEnqueueResumePendingResult { - /// 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; } + /// True when a wake item was newly queued. + [JsonPropertyName("queued")] + public bool Queued { get; set; } } -/// Parameters for the heaviest-messages query. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataContextHeaviestMessagesRequest +internal sealed class SessionQueueEnqueueResumePendingRequest { - /// 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; } -/// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataRecordContextChangeResult +internal sealed class SessionQueueProcessRequest { + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Updated working-directory/git context to record on the session. +/// Cursor, batch size, and optional long-poll/filter parameters for reading session events. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataRecordContextChangeRequest +internal sealed class EventLogReadRequest { - /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. - [JsonPropertyName("context")] - public SessionWorkingDirectoryContext Context { get => field ??= new(); set; } + /// 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; } /// 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; } } -/// 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. +/// 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 MetadataSetWorkingDirectoryResult +public sealed class EventLogTailResult { - /// Working directory after the update. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// 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; } -/// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataSetWorkingDirectoryRequest +internal sealed class SessionEventLogTailRequest { /// 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. +/// Opaque handle representing an event-type interest registration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataRecomputeContextTokensResult +public sealed class RegisterEventInterestResult { - /// 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; } + /// 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; } -/// Model identifier to use when re-tokenizing the session's existing messages. +/// Event type to register consumer interest for, used by runtime gating logic. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataRecomputeContextTokensRequest +internal sealed class RegisterEventInterestParams { - /// 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 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; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Availability of built-in job tools surfaced to boundary consumers. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsBuiltInToolAvailabilitySnapshot +public sealed class EventLogReleaseInterestResult { - /// 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; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Redacted job settings for a session. The job nonce is excluded. +/// Opaque handle previously returned by `registerInterest` to release. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsJobSnapshot +internal sealed class ReleaseEventInterestParams { - /// Availability of job-specific built-in tools. - [JsonPropertyName("builtInToolAvailability")] - public SessionSettingsBuiltInToolAvailabilitySnapshot? BuiltInToolAvailability { get; set; } + /// 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; - /// GitHub Actions event type for the job. - [JsonPropertyName("eventType")] - public string? EventType { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether this is the workflow's trigger job. - [JsonPropertyName("isTriggerJob")] - public bool? IsTriggerJob { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionUsageGetMetricsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Redacted model routing settings for a session. +/// Internal absolute code-change totals reported by the owning host. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsModelSnapshot +internal sealed class SessionUsageSetCodeChangesRequest { - /// Agent service callback URL for job and progress updates. - [JsonPropertyName("callbackUrl")] - public string? CallbackUrl { get; set; } + /// Absolute changed-file count; omission preserves the previous count. + [JsonPropertyName("filesCount")] + public double? FilesCount { get; set; } - /// Default reasoning effort for the selected model. - [JsonPropertyName("defaultReasoningEffort")] - public string? DefaultReasoningEffort { get; set; } + /// Absolute added-line total, replacing the previous reading. + [JsonPropertyName("linesAdded")] + public double LinesAdded { get; set; } - /// Agent job identifier for the session. - [JsonPropertyName("instanceId")] - public string? InstanceId { get; set; } + /// Absolute removed-line total, replacing the previous reading. + [JsonPropertyName("linesRemoved")] + public double LinesRemoved { get; set; } - /// 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. +/// Existing delegation guidance for an account without delegation access. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsOnlineEvaluationSnapshot +public sealed class SessionQuotaDelegateWarning { - /// Whether online evaluation is disabled. - [JsonPropertyName("disableOnlineEvaluation")] - public bool? DisableOnlineEvaluation { get; set; } + /// User-facing guidance. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; - /// Whether online-evaluation output-file generation is enabled. - [JsonPropertyName("enableOnlineEvaluationOutputFile")] - public bool? EnableOnlineEvaluationOutputFile { get; set; } + /// Link to upgrade the account. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; } -/// Redacted repository and GitHub host settings for a session. +/// Session quota snapshot, preserving the incumbent numeric units and reset metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsRepoSnapshot +public sealed class SessionQuotaSnapshot { - /// Checked-out repository branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Entitled quantity in this quota's units. + [JsonPropertyName("entitlementRequests")] + public double EntitlementRequests { get; set; } - /// Checked-out commit SHA. - [JsonPropertyName("commit")] - public string? Commit { get; set; } + /// Whether the provider reports available quota. + [JsonPropertyName("hasQuota")] + public bool? HasQuota { get; set; } - /// GitHub server host name. - [JsonPropertyName("host")] - public string? Host { get; set; } + /// Whether the entitlement is unlimited. + [JsonPropertyName("isUnlimitedEntitlement")] + public bool IsUnlimitedEntitlement { get; set; } - /// Protocol used to access the GitHub host. - [JsonPropertyName("hostProtocol")] - public string? HostProtocol { get; set; } + /// Additional usage in this quota's units. + [JsonPropertyName("overage")] + public double Overage { get; set; } - /// GitHub repository database ID. - [JsonPropertyName("id")] - public double? Id { get; set; } + /// Whether additional usage is allowed after quota exhaustion. + [JsonPropertyName("overageAllowedWithExhaustedQuota")] + public bool OverageAllowedWithExhaustedQuota { get; set; } - /// Repository name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Additional-usage budget cap, when provided. + [JsonPropertyName("overageEntitlement")] + public double? OverageEntitlement { get; set; } - /// GitHub repository owner database ID. - [JsonPropertyName("ownerId")] - public double? OwnerId { get; set; } + /// Percentage of the entitlement remaining. + [JsonPropertyName("remainingPercentage")] + public double RemainingPercentage { get; set; } - /// Repository owner login. - [JsonPropertyName("ownerName")] - public string? OwnerName { get; set; } + /// Quota reset time in milliseconds since the Unix epoch, when known. + [JsonPropertyName("resetDateEpochMs")] + public double? ResetDateEpochMs { get; set; } - /// Number of commits in the pull request. - [JsonPropertyName("prCommitCount")] - public double? PrCommitCount { get; set; } + /// Whether the reset time is estimated. + [JsonPropertyName("resetDateEstimated")] + public bool? ResetDateEstimated { get; set; } - /// Whether the repository is writable. - [JsonPropertyName("readWrite")] - public bool? ReadWrite { get; set; } + /// Whether this quota uses token-based billing. + [JsonPropertyName("tokenBasedBilling")] + public bool? TokenBasedBilling { get; set; } - /// GitHub secret-scanning service URL. - [JsonPropertyName("secretScanningUrl")] - public string? SecretScanningUrl { get; set; } + /// Whether usage is allowed after quota exhaustion. + [JsonPropertyName("usageAllowedWithExhaustedQuota")] + public bool UsageAllowedWithExhaustedQuota { get; set; } - /// GitHub server base URL. - [JsonPropertyName("serverUrl")] - public string? ServerUrl { get; set; } + /// Consumed quantity in this quota's units. + [JsonPropertyName("usedRequests")] + public double UsedRequests { get; set; } } -/// Redacted validation and memory-tool settings for a session. +/// The incumbent session-owned quota and account projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsValidationSnapshot +public sealed class SessionQuotaGetResult { - /// 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 account can sign up for Copilot Free. + [JsonPropertyName("canSignupForCopilotFree")] + public bool CanSignupForCopilotFree { get; set; } - /// Whether the memory-vote tool is enabled. - [JsonPropertyName("memoryVoteEnabled")] - public bool? MemoryVoteEnabled { get; set; } + /// Whether the account can delegate tasks to GitHub. + [JsonPropertyName("delegateAvailable")] + public bool DelegateAvailable { get; set; } - /// Whether secret-scanning validation is enabled. - [JsonPropertyName("secretScanningEnabled")] - public bool? SecretScanningEnabled { get; set; } + /// Existing delegation warning for a free account. + [JsonPropertyName("delegateWarning")] + public SessionQuotaDelegateWarning? DelegateWarning { get; set; } - /// General validation timeout budget in seconds. - [JsonPropertyName("timeout")] - public double? Timeout { get; set; } -} + /// Whether dynamic workflows are enabled for the active account. + [JsonPropertyName("dynamicWorkflowsEnabled")] + public bool DynamicWorkflowsEnabled { get; set; } -/// 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)] -internal sealed class SessionSettingsSnapshot -{ - /// Name of the SDK client that created the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Whether dynamic workflows are visible under the session's feature flags. + [JsonPropertyName("dynamicWorkflowsUiVisible")] + public bool DynamicWorkflowsUiVisible { get; set; } - /// Redacted job settings. - [JsonPropertyName("job")] - public SessionSettingsJobSnapshot Job { get => field ??= new(); set; } + /// Whether the account uses the free limited Copilot plan. + [JsonPropertyName("isFreeUser")] + public bool IsFreeUser { get; set; } - /// Redacted model routing settings. - [JsonPropertyName("model")] - public SessionSettingsModelSnapshot Model { get => field ??= new(); set; } + /// Whether the account uses token-based billing. + [JsonPropertyName("isTbbUser")] + public bool IsTbbUser { get; set; } - /// Online-evaluation settings safe for SDK consumers. - [JsonPropertyName("onlineEvaluation")] - public SessionSettingsOnlineEvaluationSnapshot OnlineEvaluation { get => field ??= new(); set; } + /// Whether model cost columns should be shown. + [JsonPropertyName("modelCostColumnVisible")] + public bool ModelCostColumnVisible { get; set; } - /// Redacted repository and host settings. - [JsonPropertyName("repo")] - public SessionSettingsRepoSnapshot Repo { get => field ??= new(); set; } + /// Resolved Copilot plan tier. + [JsonPropertyName("planTier")] + public SessionQuotaPlanTier PlanTier { get; set; } - /// Session start time as Unix epoch milliseconds. - [JsonPropertyName("startTimeMs")] - public double? StartTimeMs { get; set; } + /// Whether premium-request costs are billable. + [JsonPropertyName("premiumRequestsBillable")] + public bool PremiumRequestsBillable { get; set; } - /// Session timeout in milliseconds. - [JsonPropertyName("timeoutMs")] - public double? TimeoutMs { get; set; } + /// Provider-owned account quota state, including snapshots without numeric balances. + [JsonPropertyName("providerQuotas")] + public IList? ProviderQuotas { get; set; } - /// Redacted validation and memory-tool settings. - [JsonPropertyName("validation")] - public SessionSettingsValidationSnapshot Validation { get => field ??= new(); set; } + /// Known quota snapshots, keyed by the provider's quota type. + [JsonPropertyName("snapshots")] + public IDictionary Snapshots { get => field ??= new Dictionary(); 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; } + /// Upgrade link for a free account. + [JsonPropertyName("upgradeUrl")] + public string? UpgradeUrl { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsSnapshotRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Result of evaluating a Rust-owned settings predicate. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsEvaluatePredicateResult -{ - /// Whether the named settings predicate evaluated to enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } -} - -/// Named Rust-owned settings predicate to evaluate for this session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsEvaluatePredicateRequest +internal sealed class SessionQuotaGetRequest { - /// 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. +/// The incumbent session-owned quota and account projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContentExclusionPathCheck +public sealed class SessionQuotaRefreshResult { - /// Whether the session's complete content-exclusion policy excludes the path. - [JsonPropertyName("excluded")] - public bool Excluded { get; set; } + /// Whether the account can sign up for Copilot Free. + [JsonPropertyName("canSignupForCopilotFree")] + public bool CanSignupForCopilotFree { get; set; } - /// The path supplied by the caller. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; -} + /// Whether the account can delegate tasks to GitHub. + [JsonPropertyName("delegateAvailable")] + public bool DelegateAvailable { get; set; } -/// Batch content-exclusion result. Callers must fail closed when policy evaluation is unavailable. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContentExclusionCheckPathsResult -{ - /// 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; } + /// Existing delegation warning for a free account. + [JsonPropertyName("delegateWarning")] + public SessionQuotaDelegateWarning? DelegateWarning { get; set; } - /// Per-path decisions in request order. Empty when available is false. - [JsonPropertyName("checks")] - public IList Checks { get => field ??= []; set; } -} + /// Whether dynamic workflows are enabled for the active account. + [JsonPropertyName("dynamicWorkflowsEnabled")] + public bool DynamicWorkflowsEnabled { get; set; } -/// Local file system absolute paths within the session working directory to check against its content-exclusion policy. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ContentExclusionCheckPathsRequest -{ - /// 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; } + /// Whether dynamic workflows are visible under the session's feature flags. + [JsonPropertyName("dynamicWorkflowsUiVisible")] + public bool DynamicWorkflowsUiVisible { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether the account uses the free limited Copilot plan. + [JsonPropertyName("isFreeUser")] + public bool IsFreeUser { get; set; } -/// Identifier of the spawned shell process, usable with shell.kill while the process is running. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellExecResult -{ - /// Identifier usable with shell.kill while the process is running. - [JsonPropertyName("processId")] - public string ProcessId { get; set; } = string.Empty; -} + /// Whether the account uses token-based billing. + [JsonPropertyName("isTbbUser")] + public bool IsTbbUser { get; set; } -/// Shell command to run, with optional working directory and timeout in milliseconds. Spawn failures return an RPC error. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellExecRequest -{ - /// Shell command to execute. - [JsonPropertyName("command")] - public string Command { get; set; } = string.Empty; + /// Whether model cost columns should be shown. + [JsonPropertyName("modelCostColumnVisible")] + public bool ModelCostColumnVisible { get; set; } - /// Working directory (defaults to session working directory). - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Resolved Copilot plan tier. + [JsonPropertyName("planTier")] + public SessionQuotaPlanTier PlanTier { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether premium-request costs are billable. + [JsonPropertyName("premiumRequestsBillable")] + public bool PremiumRequestsBillable { get; set; } - /// Timeout in milliseconds (default: 30000). - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("timeout")] - public TimeSpan? Timeout { get; set; } -} + /// Provider-owned account quota state, including snapshots without numeric balances. + [JsonPropertyName("providerQuotas")] + public IList? ProviderQuotas { get; set; } -/// Indicates whether the signal was delivered; false if the process was unknown or already exited. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellKillResult -{ - /// Whether the signal was sent successfully. - [JsonPropertyName("killed")] - public bool Killed { 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; } } -/// 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 SessionQuotaRefreshRequest { - /// 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. +/// A pending session quota warning, consumed once by takeWarnings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UserRequestedShellCommandResult +public sealed class QuotaWarningProjection { - /// Error output when the execution failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Process exit code, when available. - [JsonPropertyName("exitCode")] - public long? ExitCode { get; set; } - - /// Captured command output. - [JsonPropertyName("output")] - public string Output { get; set; } = string.Empty; + /// User-facing warning text. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Whether the command completed successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Related account or upgrade link. + [JsonPropertyName("url")] + public string? Url { get; set; } - /// Tool call id emitted for the shell execution. - [JsonPropertyName("toolCallId")] - public string ToolCallId { get; set; } = string.Empty; + /// Existing warning category. + [JsonPropertyName("warningType")] + public string WarningType { get; set; } = string.Empty; } -/// User-requested shell command and cancellation handle. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellExecuteUserRequestedRequest +internal sealed class SessionQuotaTakeWarningsRequest { - /// 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; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Cancellation result for a user-requested shell command. +/// 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 CancelUserRequestedShellCommandResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(SessionLimitPredictionResultAvailable), "available")] +[JsonDerivedType(typeof(SessionLimitPredictionResultUnavailable), "unavailable")] +public partial class SessionLimitPredictionResult { - /// Whether an in-flight execution was found and signalled to cancel. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// User-requested shell execution cancellation handle. + +/// Baseline data provenance for a prediction. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellCancelUserRequestedRequest +public sealed class SessionLimitPredictionBaselineData { - /// Request ID previously passed to executeUserRequested. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// End of the baseline data slice. + [JsonPropertyName("windowEnd")] + public string WindowEnd { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Start of the baseline data slice. + [JsonPropertyName("windowStart")] + public string WindowStart { get; set; } = string.Empty; } -/// Post-compaction context window usage breakdown. +/// Semantic usage tier and its AI-credit cap. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryCompactContextWindow +public sealed class SessionLimitPredictionTierOption { - /// Token count from non-system messages (user, assistant, tool). - [JsonPropertyName("conversationTokens")] - public long? ConversationTokens { get; set; } + /// AI-credit cap for this tier. + [JsonPropertyName("cap")] + public double Cap { get; set; } - /// Current total tokens in the context window (system + conversation + tool definitions). - [JsonPropertyName("currentTokens")] - public long CurrentTokens { get; set; } + /// Semantic usage tier. + [JsonPropertyName("tier")] + public SessionLimitPredictionTier Tier { get; set; } +} - /// Current number of messages in the conversation. - [JsonPropertyName("messagesLength")] - public long MessagesLength { get; 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; } - /// Token count from system message(s). - [JsonPropertyName("systemTokens")] - public long? SystemTokens { get; set; } + /// Client population used for the prediction. + [JsonPropertyName("clientType")] + public SessionLimitPredictionClientType ClientType { get; set; } - /// Maximum token count for the model's context window. - [JsonPropertyName("tokenLimit")] - public long TokenLimit { get; set; } + /// Resolved model family when known. + [JsonPropertyName("family")] + public string? Family { get; set; } - /// Token count from tool definitions. - [JsonPropertyName("toolDefinitionsTokens")] - public long? ToolDefinitionsTokens { get; set; } -} + /// Model identifier used for lookup. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; -/// 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; } + /// Recommended maximum AI credits for this session. + [JsonPropertyName("recommendedCap")] + public double RecommendedCap { get; set; } - /// Number of messages removed during compaction. - [JsonPropertyName("messagesRemoved")] - public long MessagesRemoved { get; set; } + /// Tier chosen as the recommended cap. + [JsonPropertyName("recommendedTier")] + public SessionLimitPredictionTier RecommendedTier { get; set; } - /// Whether compaction completed successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Baseline fallback level used to create the prediction. + [JsonPropertyName("source")] + public SessionLimitPredictionSource Source { 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; } + /// Key matched at the source level, such as a model id, family id, or `global`. + [JsonPropertyName("sourceKey")] + public string SourceKey { get; set; } = string.Empty; - /// Number of tokens freed by compaction. - [JsonPropertyName("tokensRemoved")] - public long TokensRemoved { get; set; } + /// Ordered usage tiers and their AI-credit caps. + [JsonPropertyName("tiers")] + public IList Tiers { get => field ??= []; set; } } -/// RPC data type for SessionHistoryCompact operations. +/// The available variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionHistoryCompactRequest +public partial class SessionLimitPredictionResultAvailable : SessionLimitPredictionResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "available"; - /// 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; } + /// Predicted session limit details. + [JsonPropertyName("prediction")] + public required SessionLimitPredictionDetails Prediction { get; set; } } -/// RPC data type for SessionHistoryCompactRequestWithSession operations. +/// The unavailable variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryCompactRequestWithSession +public partial class SessionLimitPredictionResultUnavailable : SessionLimitPredictionResult { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "unavailable"; - /// 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; } + /// Reason no prediction is available. + [JsonPropertyName("reason")] + public required SessionLimitPredictionUnavailableReason Reason { get; set; } } -/// Number of events that were removed by the truncation. +/// RPC data type for SessionLimitPredictionPredict operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryTruncateResult +public sealed class SessionLimitPredictionPredictRequest { - /// Failure detail when checkpointCleanupFailed is true. - [JsonPropertyName("checkpointCleanupError")] - public string? CheckpointCleanupError { 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; } + /// Client type to size for. Defaults to `cli-interactive`. + [JsonPropertyName("clientType")] + public SessionLimitPredictionClientType? ClientType { get; set; } - /// Number of events that were removed. - [JsonPropertyName("eventsRemoved")] - public long EventsRemoved { get; set; } + /// Optional model identifier override. If omitted, the session's current model is used. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } } -/// Identifier of the event to truncate to; this event and all later events are removed. +/// RPC data type for SessionLimitPredictionPredictRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryTruncateRequest +internal sealed class SessionLimitPredictionPredictRequestWithSession { - /// 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; + /// Client type to size for. Defaults to `cli-interactive`. + [JsonPropertyName("clientType")] + public SessionLimitPredictionClientType? ClientType { 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; } -/// A root user turn that the session can rewind to. +/// GitHub URL for the session and a flag indicating whether remote steering is enabled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryRewindPoint +public sealed class RemoteEnableResult { - /// Whether at least one file in this turn or a later turn can be restored. - [JsonPropertyName("canRestoreFiles")] - public bool CanRestoreFiles { get; set; } - - /// ID of the user.message event that begins the discarded suffix. - [JsonPropertyName("eventId")] - public string EventId { get; set; } = string.Empty; - - /// Number of unique files in this turn and all later turns that have captured changes. - [JsonPropertyName("fileCount")] - public long FileCount { get; set; } - - /// Whether this turn was an automatically injected autopilot continuation. - [JsonPropertyName("isAutopilotContinuation")] - public bool IsAutopilotContinuation { get; set; } - - /// Lines added by this turn's captured file changes. - [JsonPropertyName("linesAdded")] - public long LinesAdded { get; set; } - - /// Lines removed by this turn's captured file changes. - [JsonPropertyName("linesRemoved")] - public long LinesRemoved { get; set; } - - /// ISO timestamp of the user turn. - [JsonPropertyName("timestamp")] - public string Timestamp { get; set; } = string.Empty; - - /// Whether this turn itself captured any file changes. - [JsonPropertyName("turnChangedFiles")] - public bool TurnChangedFiles { get; set; } + /// Whether remote steering is enabled. + [JsonPropertyName("remoteSteerable")] + public bool RemoteSteerable { get; set; } - /// User-visible message text for the turn. - [JsonPropertyName("userMessage")] - public string UserMessage { get; set; } = string.Empty; + /// GitHub frontend URL for this session. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// Rewind points and file-change-tracking availability for the session. +/// Optional remote session mode ("off", "export", or "on"); defaults to enabling both export and remote steering. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryListRewindPointsResult +internal sealed class RemoteEnableRequest { - /// Whether this session captured file changes from its first turn. - [JsonPropertyName("fileChangeTrackingEnabled")] - public bool FileChangeTrackingEnabled { get; set; } - - /// Root user turns in chronological order. Empty when `unavailableReason` is set. - [JsonPropertyName("points")] - public IList Points { get => field ??= []; set; } + /// 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; } - /// 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; } + /// 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 SessionHistoryListRewindPointsRequest +internal sealed class SessionRemoteDisableRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A file that a conversation-and-files rewind would restore. +/// 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 HistoryRewindFilePreview +public sealed class RemoteNotifySteerableChangedResult { - /// Aggregate change made across the discarded turns. - [JsonPropertyName("changeType")] - public HistoryRewindChangeType ChangeType { get; set; } - - /// 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; } +/// 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; } - /// Absolute path of the captured file. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Files and aggregate changes for a prospective rewind. +/// Current sharing status and shareable GitHub URL for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryPreviewRewindResult +public sealed class VisibilityGetResult { - /// 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; } - - /// Number of unique files in the preview. - [JsonPropertyName("fileCount")] - public long FileCount { 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; } - /// Files ordered by path. - [JsonPropertyName("files")] - public IList Files { get => field ??= []; 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; } - /// 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; } + /// 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; } } -/// Event boundary to preview for conversation-and-files rewind. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryPreviewRewindRequest +internal sealed class SessionVisibilityGetRequest { - /// 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; } -/// A captured file that rewind intentionally left unchanged. +/// Effective sharing status and shareable GitHub URL after updating session visibility. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistorySkippedFileRestore +public sealed class VisibilitySetResult { - /// Absolute path of the skipped file. - [JsonPropertyName("path")] - public string Path { 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; } - /// Reason the file was not restored. - [JsonPropertyName("reason")] - public HistoryFileRestoreSkipReason Reason { 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; } } -/// Structured outcome of a rewind request. +/// Desired sharing status for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryRewindResult +internal sealed class VisibilitySetRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// 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; } + /// 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; } } -/// Boundary and mode for rewinding session history. +/// Scheduled prompt entry with ID, timing (`intervalMs`, `cron`, or `at`), prompt text, recurrence, and next run time. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryRewindRequest +public sealed class ScheduleEntry { - /// ID of the user.message event that begins the discarded suffix. - [JsonPropertyName("eventId")] - public string EventId { get; set; } = string.Empty; + /// Absolute fire time (epoch milliseconds) for a one-shot calendar schedule. + [JsonPropertyName("at")] + public long? At { get; set; } - /// Whether to rewind only conversation history or also restore captured files. - [JsonPropertyName("mode")] - public HistoryRewindMode Mode { get; set; } + /// 5-field cron expression for a recurring calendar schedule, evaluated in `tz`. + [JsonPropertyName("cron")] + public string? Cron { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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 long Id { get; set; } + + /// Interval between scheduled ticks, in milliseconds (relative-interval schedules). + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonPropertyName("intervalMs")] + public TimeSpan? Interval { get; set; } + + /// 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; + + /// 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; } } -/// Indicates whether an in-progress background compaction was cancelled. +/// Snapshot of the currently active recurring prompts for this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryCancelBackgroundCompactionResult +public sealed class ScheduleList { - /// 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; } + /// Active scheduled prompts, ordered by id. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryCancelBackgroundCompactionRequest +internal sealed class SessionScheduleListRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether an in-progress manual compaction was aborted. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryAbortManualCompactionResult -{ - /// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionHistoryAbortManualCompactionRequest +internal sealed class SessionScheduleHydrateRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Markdown summary of the conversation context (empty when not available). +/// Whether the session currently has an active self-paced schedule. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistorySummarizeForHandoffResult +internal sealed class ScheduleHasSelfPacedResult { - /// 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; + /// 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 SessionHistorySummarizeForHandoffRequest +internal sealed class SessionScheduleHasSelfPacedRequest { /// 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. +/// Result of registering or re-arming a scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class HistoryClearContextResult +internal sealed class ScheduleAddResult { - /// 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; } + /// 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 clearing the conversation and seeding the window that replaces it. +/// Register a relative-interval scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HistoryClearContextRequest +internal sealed class ScheduleAddRequest { - /// 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. + /// 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; } -/// User-facing pending queue entry, with kind and display text for a queued message, slash command, or model change. +/// Register a cron scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueuePendingItems +internal sealed class ScheduleAddCronRequest { - /// 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; } + /// 5-field cron expression. + [JsonPropertyName("cron")] + public string Cron { get; set; } = string.Empty; - /// Human-readable text to display for this queue entry in the UI. - [JsonPropertyName("displayText")] - public string DisplayText { get; set; } = string.Empty; + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } - /// Stable opaque id for the canonical queued item. Batch rows share one id. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// Whether this item is a queued user message or a queued slash command / model change. - [JsonPropertyName("kind")] - public QueuePendingItemsKind Kind { get; set; } + /// Whether the schedule should re-arm after each tick. Defaults to true. + [JsonPropertyName("recurring")] + public bool? Recurring { 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// 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; } + /// IANA timezone for evaluating the cron expression. + [JsonPropertyName("tz")] + public string? Tz { get; set; } } -/// Snapshot of the session's pending queued items and immediate-steering messages. +/// Register an absolute-time scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueuePendingItemsResult +internal sealed class ScheduleAddAtRequest { - /// 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; } + /// Epoch milliseconds when the prompt should fire. + [JsonPropertyName("at")] + public long At { 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; } + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { 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; } + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// 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; } -} + /// Whether the schedule should re-arm after each tick. Defaults to false. + [JsonPropertyName("recurring")] + public bool? Recurring { 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; } -/// Internal snapshot of native queue state for local session orchestration. +/// Register a self-paced scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSnapshotResult +internal sealed class ScheduleAddSelfPacedRequest { - /// 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; } + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } - /// Insertion orders for immediate steering messages, aligned with `steeringMessages`. - [JsonPropertyName("steeringMessageOrders")] - public IList? SteeringMessageOrders { get; set; } + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// Immediate steering messages waiting for an active turn. - [JsonPropertyName("steeringMessages")] - public IList SteeringMessages { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Re-arm a self-paced scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueSnapshotRequest +internal sealed class ScheduleRearmSelfPacedRequest { + /// 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; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of moving a queued item. +/// Remove a scheduled prompt by id. The result entry is omitted if the id was unknown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueMoveItemResult +public sealed class ScheduleStopResult { - /// True when the item changed position; false when it was already at the requested position. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// The removed entry, or omitted if no entry matched. + [JsonPropertyName("entry")] + public ScheduleEntry? Entry { get; set; } } -/// Parameters for moving a queued item by stable id. +/// Identifier of the scheduled prompt to remove. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueMoveItemRequest +internal sealed class ScheduleStopRequest { - /// Stable opaque queued-item id. + /// Id of the scheduled prompt to remove. [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + public long Id { 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. +/// 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 QueueInsertAtResult +public sealed class ProviderTokenAcquireResult { - /// Fresh stable opaque id assigned to the inserted item. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// The bearer token value (without the `Bearer ` prefix). + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// Serializable message fields accepted by queue.insertAt. +/// 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 QueueInsertMessage +public sealed class ProviderTokenAcquireRequest { - /// Optional explicit agent mode. When omitted, the session's current mode is assigned. - [JsonPropertyName("agentMode")] - public SendAgentMode? AgentMode { 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; } + /// 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; - /// Accepted for SendOptions compatibility but ignored; inserted items always use queued delivery semantics. - [JsonPropertyName("mode")] - public SendMode? Mode { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Accepted for SendOptions compatibility but ignored; the requested public position controls placement. - [JsonPropertyName("prepend")] - public bool? Prepend { get; 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; } +} - /// The user message text. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; +/// Parameters sent to the owning extension to execute a workflow closure. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkflowExecuteRequest +{ + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } - /// Per-turn request headers. - [JsonPropertyName("requestHeaders")] - public IDictionary? RequestHeaders { get; set; } + /// Opaque token identifying this workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Required tool name for the turn, when any. - [JsonPropertyName("requiredTool")] - public string? RequiredTool { get; set; } + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// 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; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// 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. +/// Parameters for cooperatively aborting a workflow body. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueInsertAtRequest +public sealed class WorkflowAbortRequest { - /// Queued message contents and delivery metadata. - [JsonPropertyName("message")] - public QueueInsertMessage Message { get => field ??= new(); set; } + /// Opaque token identifying the execution attempt to abort. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Zero-based position in the public visible queue. Values outside the queue clamp to an end. - [JsonPropertyName("position")] - public long Position { 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; } -/// Result of removing a queued item. +/// Whether the client authoritatively confirmed its external work stopped. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueRemoveAtResult +public sealed class ClientTaskCancelResult { - /// True when the addressed item was removed. - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// True only when the owner confirms that external work stopped before responding. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// Parameters for removing a queued item by stable id. +/// Runtime-to-owner cancellation request for a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueRemoveAtRequest +public sealed class ClientTaskCancelRequest { - /// Stable opaque ID of the queued item to remove. + /// Opaque identifier shared by coalesced cancellation callers. + [JsonPropertyName("cancellationId")] + public string CancellationId { get; set; } = string.Empty; + + /// 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; - /// Target session identifier. + /// 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; } -/// Result of editing a queued message. +/// Describes a filesystem error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueUpdateTextResult +public sealed class SessionFsError { - /// True when the stored text changed. - [JsonPropertyName("updated")] - public bool Updated { get; set; } + /// Error classification. + [JsonPropertyName("code")] + public SessionFsErrorCode Code { get; set; } + + /// Free-form detail about the error, for logging/diagnostics. + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// 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; } } -/// Parameters for editing a single queued message. +/// File content as a UTF-8 string, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueUpdateTextRequest +public sealed class SessionFsReadFileResult { - /// Optional replacement prompt displayed to the user. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// File content as UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Stable opaque ID of the queued item to edit. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } +} - /// Replacement prompt sent to the model. - [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 withdrawing a user message. +/// File bytes as standard base64, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueWithdrawMessageResult +public sealed class SessionFsReadFileBytesResult { - /// 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; } + /// Exact file bytes encoded as standard base64. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// True when the message left the queue or, for a running turn, history. - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Conditional withdrawal of a single user message, from its queue or from the running turn it started. +/// Path of the binary file to read from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueWithdrawMessageRequest +public sealed class SessionFsReadFileBytesRequest { - /// 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; + /// 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; } -/// Append to one pending steering message without changing its identity or delivery position. +/// File path, content to write, and optional mode for the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueAppendSteeringRequest +public sealed class SessionFsWriteFileRequest { - /// Mode captured at submission. Only steering messages in the same mode may be combined. - [JsonPropertyName("agentMode")] - public SendAgentMode AgentMode { 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; } - - /// Display text to append to the existing preview after a blank line. - [JsonPropertyName("displayPrompt")] - public string DisplayPrompt { get; set; } = string.Empty; - - /// 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; + /// Content to write. + [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; + /// Optional POSIX-style mode for newly created files. + [JsonPropertyName("mode")] + public long? Mode { get; set; } - /// Text to append after a blank line. - [JsonPropertyName("prompt")] - public string Prompt { 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; } -/// Result of duplicating a queued item. +/// 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 QueueDuplicateAtResult +public sealed class SessionFsWriteFileBytesRequest { - /// 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; -/// Parameters for duplicating a queued item. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueDuplicateAtRequest -{ - /// Stable opaque ID of the queued item to duplicate. - [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; } -/// 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 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 QueueSetDrainPausedRequest +public sealed class SessionFsAppendFileRequest { - /// Whether queued-lane draining should be paused. - [JsonPropertyName("paused")] - public bool Paused { 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; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of trying to steer a queued message into a live turn. +/// Indicates whether the requested path exists in the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueSendNowResult +public sealed class SessionFsExistsResult { - /// 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 path exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } } -/// Parameters for steering a queued message into a live turn. +/// Path to test for existence in the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSendNowRequest +public sealed class SessionFsExistsRequest { - /// Stable opaque ID of the queued item to steer into the live turn. - [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; } -/// Whether the native queue has pending work. +/// Filesystem metadata for the requested path, or a filesystem error if the stat failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueHasPendingResult +public sealed class SessionFsStatResult { - /// True when queued or immediate native work is pending. - [JsonPropertyName("hasPending")] - public bool HasPending { get; set; } + /// ISO 8601 timestamp of creation. + [JsonPropertyName("birthtime")] + public DateTimeOffset Birthtime { get; set; } + + /// 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; } } -/// Identifies the target session. +/// Path whose metadata should be returned from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueHasPendingRequest +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; } -/// Whether a deferred-idle drain should run. +/// 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 QueueBeginDeferredIdleDrainResult +public sealed class SessionFsMkdirRequest { - /// True when the host should run finishDeferredIdleDrain asynchronously. - [JsonPropertyName("shouldDrain")] - public bool ShouldDrain { get; set; } -} + /// Optional POSIX-style mode for newly created directories. + [JsonPropertyName("mode")] + public long? Mode { get; 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; } + /// 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; } -/// Action selected by the native deferred-idle drain. +/// Names of entries in the requested directory, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueFinishDeferredIdleDrainResult +public sealed class SessionFsReaddirResult { - /// Whether the deferred idle was caused by an aborted foreground turn. - [JsonPropertyName("aborted")] - public bool Aborted { get; set; } + /// Entry names in the directory. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; 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; } } -/// Inputs for completing a deferred-idle drain. +/// Directory path whose entries should be listed from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueFinishDeferredIdleDrainRequest +public sealed class SessionFsReaddirRequest { - /// 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; } + /// 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 entry returned by session filesystem `readdirWithTypes`, with name and entry type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueDeferSessionIdleRequest +public sealed class SessionFsReaddirWithTypesEntry { - /// Whether the deferred idle was caused by an aborted foreground turn. - [JsonPropertyName("aborted")] - public bool Aborted { 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; } } -/// Indicates whether a user-facing pending item was removed. +/// 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 QueueRemoveMostRecentResult +public sealed class SessionFsReaddirWithTypesResult { - /// 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; } -} + /// Directory entries with type information. + [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 (with type information) should be listed from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueClearRequest +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; } -/// Internal filter for consuming queued system notifications. +/// 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 QueueConsumeSystemNotificationsRequest +public sealed class SessionFsRmRequest { - /// Opaque runtime-owned filter object. - [JsonPropertyName("filter")] - public JsonElement Filter { get; set; } + /// Ignore errors if the path does not exist. + [JsonPropertyName("force")] + public bool? Force { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; -/// Result of enqueueing the resume-pending wake item. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueEnqueueResumePendingResult -{ - /// True when a wake item was newly queued. - [JsonPropertyName("queued")] - public bool Queued { get; set; } -} + /// Remove directories and their contents recursively. + [JsonPropertyName("recursive")] + public bool? Recursive { 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; } -/// Identifies the target session. +/// 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 SessionQueueProcessRequest +public sealed class SessionFsRenameRequest { + /// 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; + + /// Source path using SessionFs conventions. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; } -/// Cursor, batch size, and optional long-poll/filter parameters for reading session events. +/// Query results including rows, columns, and rows affected, or a filesystem error if execution failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EventLogReadRequest +public sealed class SessionFsSqliteQueryResult { - /// 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; } + /// Column names from the result set. + [JsonPropertyName("columns")] + public IList Columns { get => field ??= []; 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; } + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { 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; } + /// SQLite last_insert_rowid() value for INSERT. + [JsonPropertyName("lastInsertRowid")] + public long? LastInsertRowid { 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; } + /// For SELECT: array of row objects. For others: empty array. + [JsonPropertyName("rows")] + public IList> Rows { get => field ??= []; 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; } + /// Number of rows affected (for INSERT/UPDATE/DELETE). + [JsonPropertyName("rowsAffected")] + public long RowsAffected { get; set; } +} - /// Maximum number of events to return in this batch (1–1000, default 200). - [JsonPropertyName("max")] - public long? Max { get; set; } +/// 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; } + + /// 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; - - /// 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; } } -/// 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). +/// Classified SQLite transaction failure. busyOrLocked guarantees rollback; postCommitAmbiguous must never be retried. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EventLogTailResult +public sealed class SessionFsSqliteTransactionError { - /// 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; + /// 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; } -/// Identifies the target session. +/// Per-statement results, or a classified transaction error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionEventLogTailRequest +public sealed class SessionFsSqliteTransactionResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Classified transaction failure, when execution did not succeed. + [JsonPropertyName("error")] + public SessionFsSqliteTransactionError? Error { get; set; } + + /// Per-statement query results in input order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// Opaque handle representing an event-type interest registration. +/// One statement in an atomic SQLite transaction. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RegisterEventInterestResult +public sealed class SessionFsSqliteTransactionStatement { - /// 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; + /// Optional named bind parameters. + [JsonPropertyName("params")] + public IDictionary? Params { get; set; } + + /// SQL statement to execute. + [JsonPropertyName("query")] + public string Query { get; set; } = string.Empty; + + /// How to execute the statement. + [JsonPropertyName("queryType")] + public SessionFsSqliteQueryType QueryType { get; set; } } -/// Event type to register consumer interest for, used by runtime gating logic. +/// Statements to execute atomically. Providers apply busy handling for every call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class RegisterEventInterestParams +public sealed class SessionFsSqliteTransactionRequest { - /// 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; - /// 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 EventLogReleaseInterestResult -{ - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Ordered SQL statements to execute in one transaction. + [JsonPropertyName("statements")] + public IList Statements { get => field ??= []; set; } } -/// Opaque handle previously returned by `registerInterest` to release. +/// Indicates whether the per-session SQLite database already exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ReleaseEventInterestParams +public sealed class SessionFsSqliteExistsResult { - /// 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; + /// Whether the session database already exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } } /// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUsageGetMetricsRequest +public sealed class SessionFsSqliteExistsRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Internal absolute code-change totals reported by the owning host. +/// Canvas open result returned by the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUsageSetCodeChangesRequest +public sealed class CanvasProviderOpenResult { - /// 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; } + /// Provider-supplied status text. + [JsonPropertyName("status")] + public string? Status { get; set; } - /// Absolute removed-line total, replacing the previous reading. - [JsonPropertyName("linesRemoved")] - public double LinesRemoved { get; set; } + /// Provider-supplied title. + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// URL for web-rendered canvases. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// Existing delegation guidance for an account without delegation access. +/// Host capabilities. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaDelegateWarning +public sealed class CanvasHostContextCapabilities { - /// User-facing guidance. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; - - /// Link to upgrade the account. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + /// Whether canvas rendering is supported. + [JsonPropertyName("canvases")] + public bool? Canvases { get; set; } } -/// Session quota snapshot, preserving the incumbent numeric units and reset metadata. +/// Host context supplied by the runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaSnapshot +public sealed class CanvasHostContext { - /// 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; } + /// Host capabilities. + [JsonPropertyName("capabilities")] + public CanvasHostContextCapabilities? Capabilities { get; set; } } -/// The incumbent session-owned quota and account projection. +/// Session context supplied by the runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionQuotaGetResult +public sealed class CanvasSessionContext { - /// 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; } - - /// Whether the account uses token-based billing. - [JsonPropertyName("isTbbUser")] - public bool IsTbbUser { get; set; } + /// 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; + } + + /// 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"); + + /// 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"); - /// 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; } -} + /// The runtime could not read the certificate authority or the OS trust store. + public static SandboxProxyCaState Error { get; } = new("error"); -/// 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; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SandboxProxyCaState left, SandboxProxyCaState right) => left.Equals(right); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SandboxProxyCaState left, SandboxProxyCaState right) => !(left == right); -/// 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; + /// + public override bool Equals(object? obj) => obj is SandboxProxyCaState other && Equals(other); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public bool Equals(SandboxProxyCaState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// File bytes as standard base64, or a filesystem error if the read failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReadFileBytesResult -{ - /// Exact file bytes encoded as standard base64. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// + public override string ToString() => 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; + /// 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)); + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, SandboxProxyCaState value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SandboxProxyCaState)); + } + } } -/// File path, content to write, and optional mode for the client-provided session filesystem. + +/// What the one-time staff update-channel migration did. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsWriteFileRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct StaffUpdateChannelMigrationStatus : IEquatable { - /// Content to write. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + private readonly string? _value; - /// Optional POSIX-style mode for newly created files. - [JsonPropertyName("mode")] - public long? Mode { 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 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, 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; + /// 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, 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; + /// 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); -/// 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; } -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// Path to test for existence in the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsExistsRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + 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 StaffUpdateChannelMigrationStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, StaffUpdateChannelMigrationStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(StaffUpdateChannelMigrationStatus)); + } + } } -/// Filesystem metadata for the requested path, or a filesystem error if the stat failed. + +/// What the one-time staff log-level migration did. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsStatResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct StaffLogLevelMigrationStatus : IEquatable { - /// ISO 8601 timestamp of creation. - [JsonPropertyName("birthtime")] - public DateTimeOffset Birthtime { get; 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 StaffLogLevelMigrationStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Whether the path is a directory. - [JsonPropertyName("isDirectory")] - public bool IsDirectory { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Whether the path is a file. - [JsonPropertyName("isFile")] - public bool IsFile { 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"); - /// ISO 8601 timestamp of last modification. - [JsonPropertyName("mtime")] - public DateTimeOffset Mtime { get; set; } + /// The migration already ran; the stored completion timestamp is returned unchanged. + public static StaffLogLevelMigrationStatus Current { get; } = new("current"); - /// File size in bytes. - [JsonPropertyName("size")] - public long Size { 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"); -/// 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; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(StaffLogLevelMigrationStatus left, StaffLogLevelMigrationStatus 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 !=(StaffLogLevelMigrationStatus left, StaffLogLevelMigrationStatus right) => !(left == right); -/// 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 bool Equals(object? obj) => obj is StaffLogLevelMigrationStatus other && Equals(other); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public bool Equals(StaffLogLevelMigrationStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Create parent directories as needed. - [JsonPropertyName("recursive")] - public bool? Recursive { 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; -/// Names of entries in the requested directory, or a filesystem error if the read failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirResult -{ - /// Entry names in the directory. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; 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)); + } - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } + /// + public override void Write(Utf8JsonWriter writer, StaffLogLevelMigrationStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(StaffLogLevelMigrationStatus)); + } + } } -/// Directory path whose entries should be listed from the client-provided session filesystem. + +/// What the one-time obsolete-settings cleanup did to the document. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct UserSettingsMigrateObsoleteOnDiskStatus : 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 UserSettingsMigrateObsoleteOnDiskStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// 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; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Entry type. - [JsonPropertyName("type")] - public SessionFsReaddirWithTypesEntryType Type { get; set; } -} + /// No settings document exists, so there was nothing to clean. + public static UserSettingsMigrateObsoleteOnDiskStatus Missing { get; } = new("missing"); -/// 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; } + /// 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"); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// The document parsed and held nothing obsolete, so nothing was written. + public static UserSettingsMigrateObsoleteOnDiskStatus Unchanged { get; } = new("unchanged"); -/// 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; + /// Obsolete keys were removed and the document was rewritten. + public static UserSettingsMigrateObsoleteOnDiskStatus Changed { get; } = new("changed"); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(UserSettingsMigrateObsoleteOnDiskStatus left, UserSettingsMigrateObsoleteOnDiskStatus right) => left.Equals(right); -/// 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; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(UserSettingsMigrateObsoleteOnDiskStatus left, UserSettingsMigrateObsoleteOnDiskStatus right) => !(left == right); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is UserSettingsMigrateObsoleteOnDiskStatus other && Equals(other); - /// Remove directories and their contents recursively. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// + public bool Equals(UserSettingsMigrateObsoleteOnDiskStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// Source and destination paths for renaming or moving an entry in the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsRenameRequest -{ - /// Destination path using SessionFs conventions. - [JsonPropertyName("dest")] - public string Dest { get; set; } = string.Empty; + /// + 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)); + } - /// Source path using SessionFs conventions. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, UserSettingsMigrateObsoleteOnDiskStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(UserSettingsMigrateObsoleteOnDiskStatus)); + } + } } -/// Query results including rows, columns, and rows affected, or a filesystem error if execution failed. + +/// Whether one atomic settings edit changed a file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteQueryResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SettingsEditStatus : IEquatable { - /// Column names from the result set. - [JsonPropertyName("columns")] - public IList Columns { 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 SettingsEditStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// SQLite last_insert_rowid() value for INSERT. - [JsonPropertyName("lastInsertRowid")] - public long? LastInsertRowid { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// For SELECT: array of row objects. For others: empty array. - [JsonPropertyName("rows")] - public IList> Rows { get => field ??= []; set; } + /// The edit changed the setting and was stored. + public static SettingsEditStatus Changed { get; } = new("changed"); - /// Number of rows affected (for INSERT/UPDATE/DELETE). - [JsonPropertyName("rowsAffected")] - public long RowsAffected { get; set; } -} + /// The setting already had the requested state. + public static SettingsEditStatus Unchanged { get; } = new("unchanged"); -/// 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; } + /// The existing settings file could not be edited safely; nothing was written. + public static SettingsEditStatus Invalid { get; } = new("invalid"); - /// SQL query to execute. - [JsonPropertyName("query")] - public string Query { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SettingsEditStatus left, SettingsEditStatus right) => left.Equals(right); - /// 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; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SettingsEditStatus left, SettingsEditStatus right) => !(left == right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is SettingsEditStatus other && Equals(other); -/// 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; } + /// + public bool Equals(SettingsEditStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Human-readable transaction failure message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// Per-statement results, or a classified transaction error. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionResult -{ - /// Classified transaction failure, when execution did not succeed. - [JsonPropertyName("error")] - public SessionFsSqliteTransactionError? Error { get; set; } + /// + public override string ToString() => Value; - /// Per-statement query results in input order. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; 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)); + } + + /// + public override void Write(Utf8JsonWriter writer, SettingsEditStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SettingsEditStatus)); + } + } } -/// One statement in an atomic SQLite transaction. + +/// One atomic settings mutation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionStatement +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SettingsEditOperation : IEquatable { - /// Optional named bind parameters. - [JsonPropertyName("params")] - public IDictionary? Params { get; set; } + private readonly string? _value; - /// SQL statement to execute. - [JsonPropertyName("query")] - public string Query { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SettingsEditOperation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// How to execute the statement. - [JsonPropertyName("queryType")] - public SessionFsSqliteQueryType QueryType { get; set; } -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// 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; + /// Set one scalar value. + public static SettingsEditOperation Set { get; } = new("set"); - /// Ordered SQL statements to execute in one transaction. - [JsonPropertyName("statements")] - public IList Statements { get => field ??= []; set; } -} + /// Add one string to a list, unless present. + public static SettingsEditOperation AddItem { get; } = new("add-item"); -/// 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; } -} + /// Remove one string from a list, unless absent. + public static SettingsEditOperation RemoveItem { get; } = new("remove-item"); -/// Identifies the target session. -public sealed class SessionFsSqliteExistsRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Remove the setting without supplying a value. + public static SettingsEditOperation Remove { get; } = new("remove"); -/// 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; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SettingsEditOperation left, SettingsEditOperation right) => left.Equals(right); - /// Provider-supplied title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SettingsEditOperation left, SettingsEditOperation right) => !(left == right); - /// URL for web-rendered canvases. - [JsonPropertyName("url")] - public string? Url { get; set; } -} + /// + public override bool Equals(object? obj) => obj is SettingsEditOperation other && Equals(other); -/// 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 bool Equals(SettingsEditOperation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(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; } -} + /// + public override string ToString() => Value; -/// 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; } + /// 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)); + } + + /// + 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 +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct RepoSettingsEditScope : IEquatable { - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; + 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; + } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// The token variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class GitHubTokenAcquireResultToken : GitHubTokenAcquireResult -{ - /// - [JsonIgnore] - public override string Kind => "token"; + /// A live fetch succeeded. + public static ManagedSettingsServerPolicyResolution Live { get; } = new("live"); - /// GitHub access token acquired by the SDK host. - [JsonPropertyName("accessToken")] - public required string AccessToken { get; set; } + /// A recent cached entry was served without a live fetch. + public static ManagedSettingsServerPolicyResolution FreshCache { get; } = new("freshCache"); + + /// 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"); - /// 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; } + /// No fetch was attempted because no usable token was available. + public static ManagedSettingsServerPolicyResolution SkippedNoToken { get; } = new("skippedNoToken"); - /// OAuth token type. Defaults to bearer when omitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("tokenType")] - public string? TokenType { get; set; } -} + /// 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); -/// Asks the SDK client to acquire a GitHub access token from an opaque callback registration. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTokenAcquireRequest -{ - /// Effective GitHub host for which the callback must return a token. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; + /// + public bool Equals(ManagedSettingsServerPolicyResolution other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Why the runtime is requesting a GitHub credential. - [JsonPropertyName("reason")] - public GitHubTokenAcquireReason Reason { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Opaque identifier generated by the SDK for this callback registration. - [JsonPropertyName("registrationId")] - public string RegistrationId { get; set; } = string.Empty; + /// + public override string ToString() => Value; - /// Session receiving the token. Absent only before a cloud session has been assigned its id. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } + /// 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)); + } + } } -/// A response is meaningful only on the connection and request that issued its challenge. -public sealed class InstallationsConfirmResult + +/// The operation that requires a policy-helper response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsPolicyHelperReason : IEquatable { - /// Exact challenge from the request. - [JsonPropertyName("confirmationId")] - public string ConfirmationId { get; set; } = string.Empty; + private readonly string? _value; - /// Fresh explicit user decision. There is no default. - [JsonPropertyName("decision")] - public InstallationDecision Decision { 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; + } - /// Exact review commitment from the request. - [JsonPropertyName("reviewFingerprint")] - public string ReviewFingerprint { get; set; } = string.Empty; -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// 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; -} + /// Resolve policy for session startup. + public static ManagedSettingsPolicyHelperReason SessionStart { get; } = new("sessionStart"); + /// Resolve policy before plugin activation. + public static ManagedSettingsPolicyHelperReason PluginActivation { get; } = new("pluginActivation"); -/// 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; -} + /// 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); -/// -/// 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 bool Equals(object? obj) => obj is ManagedSettingsPolicyHelperReason other && Equals(other); - /// Configured tool selection, not permission to invoke those tools. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// + public bool Equals(ManagedSettingsPolicyHelperReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Transport in the effective persisted remote configuration. - [JsonPropertyName("transport")] - public McpPlanRemoteTransport Transport { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Exact resolved endpoint, without templates or secret placeholders. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + /// + public override string ToString() => Value; + + /// 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)); + } + + /// + 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); @@ -28203,20 +31578,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)); } } } @@ -28741,49 +32116,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); @@ -28791,20 +32181,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)); } } } @@ -28974,6 +32364,9 @@ public CatalogCapability(string value) /// Understands opt-in `application/vnd.github.copilot-plugin` candidates and their typed identity, version, source, and compatibility fields. public static CatalogCapability AgentPluginDiscovery { get; } = new("agent-plugin-discovery"); + /// Extends agent-plugin-discovery to Claude plugin (`application/vnd.anthropic.claude-plugin+json`) and portable Agent Plugin (`application/agent-plugin+json`) candidates, each reporting its format as its media type. Requires agent-plugin-discovery; without it, plugin searches return only Copilot plugins. + public static CatalogCapability AgentPluginFormats { get; } = new("agent-plugin-formats"); + /// Understands side-effect-free MCP install-plan requests, results, and plan handles; `planning-unavailable` separately reports that planning is not enabled. public static CatalogCapability McpInstallPlanning { get; } = new("mcp-install-planning"); @@ -29004,6 +32397,12 @@ public CatalogCapability(string value) /// Understands verified Agent Finder Skill install, uninstall, recovery and installation-scoped enablement APIs. Advertised only by runtimes with the Skill installation engine linked; acquisition may still be refused as feature-disabled per selected session. public static CatalogCapability SkillConfirmedInstallation { get; } = new("skill-confirmed-installation"); + /// Understands `installability` and the reviewable `installPlan` on Agent Plugin candidates. Requires agent-plugin-discovery; without it, plugin candidates are unchanged. A plugin is installable only with complete, verified catalogue install data, and may still be reported as feature-disabled per selected session. + public static CatalogCapability AgentPluginConfirmedInstallation { get; } = new("agent-plugin-confirmed-installation"); + + /// Extends skill-confirmed-installation to planning from an Agent Finder materialisation reference through skills.planInstallFromReference, and to its distinct reference failure reasons. Requires skill-confirmed-installation. Advertised only by runtimes with the Skill installation engine linked. + public static CatalogCapability SkillReferenceInstallation { get; } = new("skill-reference-installation"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(CatalogCapability left, CatalogCapability right) => left.Equals(right); @@ -30309,6 +33708,12 @@ public CatalogMediaType(string value) /// An inert Agent Plugin descriptor. public static CatalogMediaType ApplicationVndGitHubCopilotPlugin { get; } = new("application/vnd.github.copilot-plugin"); + /// An inert Claude plugin descriptor, surfaced only under the agent-plugin-formats capability. + public static CatalogMediaType ApplicationVndAnthropicClaudePluginJson { get; } = new("application/vnd.anthropic.claude-plugin+json"); + + /// An inert portable Agent Plugin descriptor, surfaced only under the agent-plugin-formats capability. + public static CatalogMediaType ApplicationAgentPluginJson { get; } = new("application/agent-plugin+json"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(CatalogMediaType left, CatalogMediaType right) => left.Equals(right); @@ -31336,6 +34741,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))] @@ -31641,6 +35325,21 @@ public SkillInstallationFailureReason(string value) /// A storage operation failed; inspect durable state before retrying. public static SkillInstallationFailureReason WriteFailed { get; } = new("write-failed"); + /// The materialisation reference is malformed or uses an unsupported schema version. Returned only to callers that require skill-reference-installation. + public static SkillInstallationFailureReason InvalidReference { get; } = new("invalid-reference"); + + /// Agent Finder has no materialisation for the referenced resource revision (HTTP 404). Returned only to callers that require skill-reference-installation. + public static SkillInstallationFailureReason DescriptorNotFound { get; } = new("descriptor-not-found"); + + /// Agent Finder withdrew the referenced resource revision (HTTP 410). Returned only to callers that require skill-reference-installation. + public static SkillInstallationFailureReason DescriptorWithdrawn { get; } = new("descriptor-withdrawn"); + + /// The retrieved descriptor does not match the referenced descriptor or bundle digest. Returned only to callers that require skill-reference-installation. + public static SkillInstallationFailureReason DigestMismatch { get; } = new("digest-mismatch"); + + /// The referenced resource is not a Skill. Returned only to callers that require skill-reference-installation. + public static SkillInstallationFailureReason UnsupportedResource { get; } = new("unsupported-resource"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(SkillInstallationFailureReason left, SkillInstallationFailureReason right) => left.Equals(right); @@ -32008,7 +35707,157 @@ public override void Write(Utf8JsonWriter writer, CatalogAgentPluginCompatibilit } -/// Canonical Agent Plugin media type. +/// Typed installability state for an Agent Plugin candidate. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct CatalogAgentPluginInstallability : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public CatalogAgentPluginInstallability(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The candidate carries complete, verified install data and the selected session may install it. Its install plan is present. + public static CatalogAgentPluginInstallability Installable { get; } = new("installable"); + + /// The catalogue supplied no complete, verified install data, so no install target is offered. + public static CatalogAgentPluginInstallability InstallDataUnavailable { get; } = new("install-data-unavailable"); + + /// Plugin installation is understood but disabled for the selected session. + public static CatalogAgentPluginInstallability FeatureDisabled { get; } = new("feature-disabled"); + + /// Reserved for plugin formats this runtime cannot install. Every format surfaced today installs through a marketplace. + public static CatalogAgentPluginInstallability NotInstallableKind { get; } = new("not-installable-kind"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAgentPluginInstallability left, CatalogAgentPluginInstallability right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAgentPluginInstallability left, CatalogAgentPluginInstallability right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is CatalogAgentPluginInstallability other && Equals(other); + + /// + public bool Equals(CatalogAgentPluginInstallability 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 CatalogAgentPluginInstallability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, CatalogAgentPluginInstallability value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAgentPluginInstallability)); + } + } +} + + +/// A kind of component an Agent Plugin contributes. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct CatalogAgentPluginComponent : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public CatalogAgentPluginComponent(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Custom agents. + public static CatalogAgentPluginComponent Agent { get; } = new("agent"); + + /// Slash commands. + public static CatalogAgentPluginComponent Command { get; } = new("command"); + + /// Skills. + public static CatalogAgentPluginComponent Skill { get; } = new("skill"); + + /// Hooks. + public static CatalogAgentPluginComponent Hook { get; } = new("hook"); + + /// Bundled MCP servers. + public static CatalogAgentPluginComponent McpServer { get; } = new("mcp-server"); + + /// Bundled LSP servers. + public static CatalogAgentPluginComponent LspServer { get; } = new("lsp-server"); + + /// Extensions. + public static CatalogAgentPluginComponent Extension { get; } = new("extension"); + + /// Canvases. + public static CatalogAgentPluginComponent Canvas { get; } = new("canvas"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAgentPluginComponent left, CatalogAgentPluginComponent right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAgentPluginComponent left, CatalogAgentPluginComponent right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is CatalogAgentPluginComponent other && Equals(other); + + /// + public bool Equals(CatalogAgentPluginComponent 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 CatalogAgentPluginComponent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, CatalogAgentPluginComponent value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAgentPluginComponent)); + } + } +} + + +/// Media type naming the plugin format of an Agent Plugin candidate. Values other than `application/vnd.github.copilot-plugin` appear only when the caller requires the agent-plugin-formats capability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -32031,6 +35880,12 @@ public CatalogAgentPluginMediaType(string value) /// A GitHub Copilot Agent Plugin descriptor. public static CatalogAgentPluginMediaType ApplicationVndGitHubCopilotPlugin { get; } = new("application/vnd.github.copilot-plugin"); + /// A Claude plugin descriptor (`.claude-plugin/plugin.json` layout). + public static CatalogAgentPluginMediaType ApplicationVndAnthropicClaudePluginJson { get; } = new("application/vnd.anthropic.claude-plugin+json"); + + /// A portable Agent Plugin descriptor. + public static CatalogAgentPluginMediaType ApplicationAgentPluginJson { get; } = new("application/agent-plugin+json"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(CatalogAgentPluginMediaType left, CatalogAgentPluginMediaType right) => left.Equals(right); @@ -32464,285 +36319,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))] @@ -32932,204 +36508,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)); - } - } -} - - -/// 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 ManagedPermissionVerdict : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public ManagedPermissionVerdict(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Managed policy prohibits the operation; do not offer approval. - public static ManagedPermissionVerdict Deny { get; } = new("deny"); - - /// Managed policy requires the consumer to obtain approval. - public static ManagedPermissionVerdict Ask { get; } = new("ask"); - - /// Managed policy allows the operation, subject to other security controls. - public static ManagedPermissionVerdict Allow { get; } = new("allow"); - - /// 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 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 ManagedPermissionVerdict other && Equals(other); - - /// - public bool Equals(ManagedPermissionVerdict 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 ManagedPermissionVerdict Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, ManagedPermissionVerdict value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedPermissionVerdict)); - } - } -} - - /// Path conventions used by this filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -34462,90 +37840,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))] @@ -40119,6 +43413,9 @@ public SessionCapability(string value) /// Cross-session history tools and session-store SQL prompt/tool metadata. public static SessionCapability SessionStore { get; } = new("session-store"); + /// Read-only GitHub cache SQL tool (github_sql). + public static SessionCapability GitHubCacheStore { get; } = new("github-cache-store"); + /// MCP Apps UI passthrough. public static SessionCapability McpApps { get; } = new("mcp-apps"); @@ -42821,65 +46118,512 @@ public SessionLimitPredictionTier(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 override SessionLimitPredictionTier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionLimitPredictionTier value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLimitPredictionTier)); + } + } +} + + +/// Baseline fallback level used to create the prediction. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionLimitPredictionSource : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionLimitPredictionSource(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = 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 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 sealed class Converter : JsonConverter { /// - public override SessionLimitPredictionTier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionFsReaddirWithTypesEntryType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionLimitPredictionTier value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionFsReaddirWithTypesEntryType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLimitPredictionTier)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsReaddirWithTypesEntryType)); } } } -/// Baseline fallback level used to create the prediction. +/// 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 SessionLimitPredictionSource : IEquatable +public readonly struct SessionFsSqliteQueryType : 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 SessionLimitPredictionSource(string value) + public SessionFsSqliteQueryType(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 prediction used the exact resolved model's baseline cell. - public static SessionLimitPredictionSource Model { get; } = new("model"); + /// 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); @@ -42887,62 +46631,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); @@ -42950,65 +46697,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); @@ -43016,62 +46766,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); @@ -43079,62 +46829,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); @@ -43142,62 +46892,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); @@ -43205,693 +46958,1444 @@ 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; + + /// ContentExclusion APIs. + public ServerContentExclusionApi ContentExclusion => + 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 @@ -44097,6 +48601,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. @@ -44130,89 +48644,27 @@ 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. +/// Provides server-scoped ContentExclusion APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerSandboxProxyCaApi +public sealed class ServerContentExclusionApi { private readonly JsonRpc _rpc; - internal ServerSandboxProxyCaApi(JsonRpc rpc) + internal ServerContentExclusionApi(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. + /// Validates candidate content-exclusion policies and returns them normalized, without starting a session. This runs the same parser the runtime's own policy enforcement uses, so a host cannot accept a policy set the runtime would later reject, nor reject one it would have honored. Call it when policies arrive from outside the runtime — a command-line flag, a configuration file, a host's own settings — and must be checked before a session exists to apply them to. A malformed document fails with a message prefixed `Invalid JSON: `; a well-formed document that violates the policy schema fails with the offending field paths. + /// Unparsed JSON text expected to hold an array of content-exclusion policies. /// 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) + /// The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this result. + public async Task ValidatePoliciesAsync(string policiesJson, CancellationToken cancellationToken = default) { - var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; - return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.trust", [request], cancellationToken); - } + ArgumentNullException.ThrowIfNull(policiesJson); - /// 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); + var request = new ContentExclusionValidatePoliciesRequest { PoliciesJson = policiesJson }; + return await CopilotClient.InvokeRpcAsync(_rpc, "contentExclusion.validatePolicies", [request], cancellationToken); } } @@ -44301,6 +48753,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. @@ -44349,6 +48846,14 @@ 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. @@ -44509,12 +49014,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); } @@ -44522,12 +49026,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); } @@ -44600,11 +49103,9 @@ internal async Task AllocateRequestIdAsync(Cancellat /// 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) + internal async Task SearchAsync(long requestId, object? authInfo, long limit, string? query = null, string? repository = null, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(authInfo); - - var request = new McpRegistrySearchRequest { RequestId = requestId, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo)!.Value, Limit = limit, Query = query, Repository = repository }; + 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); } @@ -44729,6 +49230,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 @@ -44756,6 +49343,22 @@ public async Task PlanInstallAsync(CatalogCli return await CopilotClient.InvokeRpcAsync(_rpc, "skills.planInstall", [request], cancellationToken); } + /// Plans installation of the Skill pinned by an Agent Finder materialisation reference without writing files. The runtime retrieves the descriptor only from its fixed Agent Finder authority and applies the same verification as skills.planInstall; the review shows only verified data. Apply the returned plan with skills.applyInstall. + /// Required authenticated bound catalogue, skill-confirmed-installation and skill-reference-installation capabilities. + /// Materialisation reference to verify and plan. + /// Existing local session attached to this connection. + /// The to monitor for cancellation requests. The default is . + /// Skill installation management result with the honoured contract, or a typed refusal. + public async Task PlanInstallFromReferenceAsync(CatalogClientContract contract, CatalogMaterializationReference reference, string policySessionId, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(contract); + ArgumentNullException.ThrowIfNull(reference); + ArgumentNullException.ThrowIfNull(policySessionId); + + var request = new SkillPlanInstallFromReferenceRequest { Contract = contract, Reference = reference, PolicySessionId = policySessionId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "skills.planInstallFromReference", [request], cancellationToken); + } + /// Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled. /// Required authenticated bound catalogue and Skill installation capabilities. /// Opaque original plan, consumed once. @@ -44806,7 +49409,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 . @@ -44829,6 +49432,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 ?? @@ -45234,6 +49847,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`). @@ -45245,6 +49868,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. @@ -45304,40 +49937,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 @@ -45401,55 +50000,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 @@ -45498,11 +50048,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); } @@ -45565,115 +50113,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); - } - - /// 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 Runtime APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerRuntimeApi @@ -47278,14 +51717,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); } @@ -47319,13 +51757,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); } @@ -47352,13 +51789,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); } @@ -47554,15 +51990,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); } } @@ -47704,6 +52139,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 . @@ -48792,15 +53240,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); } @@ -49911,7 +54357,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. @@ -49938,19 +54387,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); } } @@ -50078,13 +54527,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); } @@ -50111,13 +54559,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); } @@ -50238,7 +54685,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(); @@ -50448,12 +54895,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); } @@ -51535,12 +55982,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); } @@ -52914,9 +57360,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))] @@ -53000,10 +57453,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(CapiSessionOptions))] [JsonSerializable(typeof(CardDigest))] [JsonSerializable(typeof(CatalogAgentPluginCandidateProvenance))] +[JsonSerializable(typeof(CatalogAgentPluginInstallPlan))] +[JsonSerializable(typeof(CatalogAgentPluginInstallSource))] [JsonSerializable(typeof(CatalogAiSkillCandidateProvenance))] [JsonSerializable(typeof(CatalogCandidate))] [JsonSerializable(typeof(CatalogCandidateSource))] [JsonSerializable(typeof(CatalogClientContract))] +[JsonSerializable(typeof(CatalogMaterializationReference))] [JsonSerializable(typeof(CatalogMcpServerCandidateProvenance))] [JsonSerializable(typeof(CatalogNegotiatedContract))] [JsonSerializable(typeof(CatalogPluginRepositorySource))] @@ -53019,7 +57475,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))] @@ -53060,6 +57515,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ContentExclusionCheckPathsRequest))] [JsonSerializable(typeof(ContentExclusionCheckPathsResult))] [JsonSerializable(typeof(ContentExclusionPathCheck))] +[JsonSerializable(typeof(ContentExclusionValidatePoliciesRequest))] +[JsonSerializable(typeof(ContentExclusionValidatePoliciesResult))] [JsonSerializable(typeof(ContextHeaviestMessage))] [JsonSerializable(typeof(CopilotUserResponse))] [JsonSerializable(typeof(CopilotUserResponseEndpoints))] @@ -53121,11 +57578,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))] @@ -53192,6 +57655,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))] @@ -53207,9 +57671,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))] @@ -53224,6 +57690,7 @@ 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))] @@ -53232,18 +57699,36 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [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))] @@ -53295,8 +57780,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))] @@ -53318,6 +57801,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))] @@ -53437,11 +57921,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))] @@ -53515,6 +58000,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))] @@ -53536,6 +58023,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))] @@ -53643,7 +58132,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))] @@ -53658,15 +58153,32 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [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))] @@ -53940,6 +58452,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))] @@ -53969,6 +58482,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SkillInstallationSummary))] [JsonSerializable(typeof(SkillInstallationsRequest))] [JsonSerializable(typeof(SkillList))] +[JsonSerializable(typeof(SkillPlanInstallFromReferenceRequest))] [JsonSerializable(typeof(SkillPlanInstallRequest))] [JsonSerializable(typeof(SkillPlanUninstallRequest))] [JsonSerializable(typeof(SkillSetEnabledRequest))] @@ -53986,7 +58500,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))] @@ -54050,7 +58563,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))] @@ -54062,8 +58574,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))] @@ -54112,8 +58668,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 6d7d21e0f7..b5fb20b790 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")] @@ -3822,12 +3832,6 @@ public sealed partial class SessionCompactionStartData /// Conversation compaction results including success status, metrics, and optional error details. public sealed partial class SessionCompactionCompleteData { - /// Legacy active-workflow reminder retained for replay compatibility. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonInclude] - [JsonPropertyName("activeFactorySummary")] - internal string? ActiveFactorySummary { get; set; } - /// Authoritative active-workflow reminder appended to the compacted context. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonInclude] @@ -4296,6 +4300,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; } @@ -6232,11 +6246,6 @@ public sealed partial class SubagentStartedData [JsonPropertyName("executionMode")] public string? ExecutionMode { get; set; } - /// Legacy root id of the workflow run that spawned this sub-agent. New consumers should use workflowRunId. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("factoryRunId")] - public string? FactoryRunId { get; set; } - /// Model the sub-agent will run with, when known at start. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("model")] @@ -6626,6 +6635,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")] @@ -8805,6 +8824,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. @@ -8881,6 +8905,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; } @@ -14443,6 +14477,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); diff --git a/dotnet/src/HookErrorTextConverter.cs b/dotnet/src/HookErrorTextConverter.cs new file mode 100644 index 0000000000..fb798790dd --- /dev/null +++ b/dotnet/src/HookErrorTextConverter.cs @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace GitHub.Copilot; + +/// +/// Reads the error of an errorOccurred or sessionEnd hook input as a string. +/// The CLI sends an error object ({"name", "message", "stack"} or {"message"}): +/// its message becomes the error text, and an object without a string message becomes its JSON. +/// A string passes through. +/// +internal sealed class HookErrorTextConverter : JsonConverter +{ + /// + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartObject) + { + using var document = JsonDocument.ParseValue(ref reader); + var error = document.RootElement; + return error.TryGetProperty("message", out var message) && message.ValueKind == JsonValueKind.String + ? message.GetString() + : error.GetRawText(); + } + + return reader.TokenType == JsonTokenType.String + ? reader.GetString() + : throw new JsonException( + $"Expected a string or an error object for a hook error, got {reader.TokenType}."); + } + + /// + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) => + writer.WriteStringValue(value); +} diff --git a/dotnet/src/PermissionHandlers.cs b/dotnet/src/PermissionHandlers.cs index 6ff3abbda3..17b003f8a9 100644 --- a/dotnet/src/PermissionHandlers.cs +++ b/dotnet/src/PermissionHandlers.cs @@ -40,7 +40,7 @@ private static bool RequiresManagedApproval(PermissionRequest request) or "custom-tool" or "hook" or "extension-management" - or "factory" + or "workflow" or "extension-permission-access" or "extension-env-access"); } diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index c836a095dc..2ebc2cc4cd 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -1473,6 +1473,18 @@ public sealed class HookInvocation /// public sealed class PreToolUseHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1551,6 +1563,18 @@ public sealed class PreToolUseHookOutput /// public sealed class PreMcpToolCallHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1628,6 +1652,18 @@ public sealed class PreMcpToolCallHookOutput /// public sealed class PostToolUseHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1699,6 +1735,18 @@ public sealed class PostToolUseHookOutput /// public sealed class PostToolUseFailureHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1757,6 +1805,18 @@ public sealed class PostToolUseFailureHookOutput /// public sealed class UserPromptSubmittedHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1812,6 +1872,18 @@ public sealed class UserPromptSubmittedHookOutput /// public sealed class UserPromptTransformedHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1861,6 +1933,18 @@ public sealed class UserPromptTransformedHookOutput /// public sealed class SessionStartHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1921,6 +2005,18 @@ public sealed class SessionStartHookOutput /// public sealed class SessionEndHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -1963,6 +2059,7 @@ public sealed class SessionEndHookInput /// Error message if the session ended due to an error. /// [JsonPropertyName("error")] + [JsonConverter(typeof(HookErrorTextConverter))] public string? Error { get; set; } } @@ -1995,6 +2092,18 @@ public sealed class SessionEndHookOutput /// public sealed class ErrorOccurredHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -2018,6 +2127,7 @@ public sealed class ErrorOccurredHookInput /// Error message describing what went wrong. /// [JsonPropertyName("error")] + [JsonConverter(typeof(HookErrorTextConverter))] public string Error { get; set; } = string.Empty; /// @@ -2079,6 +2189,18 @@ public sealed class ErrorOccurredHookOutput /// public sealed class AgentStopHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the session that triggered the hook. /// @@ -2140,6 +2262,18 @@ public sealed class AgentStopHookOutput /// public sealed class SubagentStartHookInput { + /// Subagent actor ID, absent for root hooks and older runtimes. + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// Subagent type, when known by the runtime. + [JsonPropertyName("agentType")] + public string? AgentType { get; set; } + + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the parent session. /// @@ -2201,6 +2335,10 @@ public sealed class SubagentStartHookOutput /// public sealed class SubagentStopHookInput { + /// Immediate parent actor ID, when available. + [JsonPropertyName("parentAgentId")] + public string? ParentAgentId { get; set; } + /// /// The runtime session ID of the parent session. /// @@ -3131,6 +3269,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. /// @@ -3623,6 +3767,7 @@ protected SessionConfigBase(SessionConfigBase? other) EnableFileHooks = other.EnableFileHooks; EnableHostGitOperations = other.EnableHostGitOperations; EnableSessionStore = other.EnableSessionStore; + EnableGitHubCacheStore = other.EnableGitHubCacheStore; EnableSkills = other.EnableSkills; EnableMcpApps = other.EnableMcpApps; GitHubMcpToolConfig = other.GitHubMcpToolConfig is null @@ -3688,6 +3833,7 @@ protected SessionConfigBase(SessionConfigBase? other) ExpAssignments = other.ExpAssignments; EnableManagedSettings = other.EnableManagedSettings; EnforceManagedModelDefaults = other.EnforceManagedModelDefaults; + InteractiveUser = other.InteractiveUser; ManagedSettings = other.ManagedSettings; SkillProvider = other.SkillProvider; #pragma warning disable GHCP001 @@ -3700,6 +3846,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; @@ -3804,9 +3952,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; } @@ -3824,6 +3972,14 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableSessionStore { get; set; } + /// + /// When , requests the read-only GitHub cache store + /// (the github-cache-store capability). The runtime registers the + /// github_sql tool only when its GITHUB_CACHE_STORE feature flag + /// is also on. Defaults to disabled. + /// + public bool? EnableGitHubCacheStore { get; set; } + /// /// When , enables skill loading, including built-in /// skills and discovered skill directories. When , no @@ -4095,9 +4251,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. @@ -4109,7 +4290,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. @@ -4242,6 +4429,15 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnforceManagedModelDefaults { get; set; } + /// + /// Declares that a human drives this session's conversation, enabling + /// user-message analysis telemetry for user-authored messages. Automated + /// messages remain excluded. Defaults to false for SDK sessions; ignored + /// for cloud sessions and when resuming a session already active in the + /// runtime process. + /// + public bool? InteractiveUser { get; set; } + /// /// Optional managed-settings layer injected at session bootstrap. Currently /// carries a permissions object that composes restrictively with any @@ -4340,6 +4536,7 @@ private SessionConfig(SessionConfig? other) : base(other) if (other is null) return; SessionId = other.SessionId; + Name = other.Name; Cloud = other.Cloud; RefreshCustomInstructions = other.RefreshCustomInstructions; } @@ -4347,6 +4544,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/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.GitHubCacheStore.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.GitHubCacheStore.cs new file mode 100644 index 0000000000..95c8d4f977 --- /dev/null +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.GitHubCacheStore.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +#if NET8_0_OR_GREATER +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed partial class ClientSessionLifetimeTests +{ + [Theory] + [InlineData(null)] + [InlineData(false)] + [InlineData(true)] + public async Task EnableGitHubCacheStore_Is_Forwarded_On_Create_And_Resume(bool? enabled) + { + 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 + { + EnableGitHubCacheStore = enabled + }); + await using var resumed = await client.ResumeSessionAsync("resumed", new ResumeSessionConfig + { + EnableGitHubCacheStore = enabled + }); + foreach (var method in new[] { "session.create", "session.resume" }) + { + var payload = Assert.Single(server.Requests, request => request.Method == method).Params; + if (enabled is null) + { + Assert.False(payload.TryGetProperty("enableGitHubCacheStore", out _)); + } + else + { + Assert.Equal(enabled.Value, payload.GetProperty("enableGitHubCacheStore").GetBoolean()); + } + } + } +} +#endif diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index c3ea694395..98d2453186 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -24,6 +24,77 @@ public sealed partial class ClientSessionLifetimeTests { private sealed record RpcRequestRecord(string Method, JsonElement Params); + [Fact] + public async Task TypedHookDispatch_RetainsChildIdentityAndOldRootInputs() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var captured = new List(); + Task Capture(object input, HookInvocation invocation) where T : class + { + captured.Add(input); + Assert.False(string.IsNullOrEmpty(invocation.SessionId)); + return Task.FromResult(null); + } + var hooks = new SessionHooks + { + OnPreToolUse = (i, c) => Capture(i, c), + OnPreMcpToolCall = (i, c) => Capture(i, c), + OnPostToolUse = (i, c) => Capture(i, c), + OnPostToolUseFailure = (i, c) => Capture(i, c), + OnUserPromptSubmitted = (i, c) => Capture(i, c), + OnUserPromptTransformed = (i, c) => Capture(i, c), + OnSessionStart = (i, c) => Capture(i, c), + OnSessionEnd = (i, c) => Capture(i, c), + OnErrorOccurred = (i, c) => Capture(i, c), + OnAgentStop = (i, c) => Capture(i, c), + OnSubagentStart = (i, c) => Capture(i, c), + OnSubagentStop = (i, c) => Capture(i, c) + }; + await using var session = await client.CreateSessionAsync(new SessionConfig { Hooks = hooks }); + foreach (var hook in new[] { "preToolUse", "preMcpToolCall", "postToolUse", "postToolUseFailure", + "userPromptSubmitted", "userPromptTransformed", "sessionStart", "sessionEnd", "errorOccurred", + "agentStop", "subagentStart", "subagentStop" }) + { + foreach (var enriched in new[] { true, false }) + { + var input = new Dictionary + { + ["sessionId"] = "reported-session", + ["timestamp"] = 1L, + ["cwd"] = "work" + }; + if (enriched) + { + input["agentId"] = "child-actor"; + input["agentType"] = "explore"; + input["parentAgentId"] = "parent-actor"; + } + captured.Clear(); + await server.SendRequestAsync("hooks.invoke", new Dictionary + { + ["sessionId"] = session.SessionId, + ["hookType"] = hook, + ["input"] = input + }).WaitAsync(TimeSpan.FromSeconds(5)); + var actual = Assert.Single(captured); + Assert.Equal("reported-session", actual.GetType().GetProperty("SessionId")!.GetValue(actual)); + foreach (var field in new Dictionary + { + ["AgentId"] = "child-actor", + ["AgentType"] = "explore", + ["ParentAgentId"] = "parent-actor" + }) + { + var property = actual.GetType().GetProperty(field.Key); + Assert.NotNull(property); + var value = property.GetValue(actual) as string; + Assert.Equal(enriched ? field.Value : "", value ?? ""); + } + } + } + } + [Theory] [InlineData(false, true)] [InlineData(false, false)] @@ -3330,6 +3401,7 @@ public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() await using var session = await client.CreateSessionAsync(new SessionConfig { EnforceManagedModelDefaults = true, + InteractiveUser = true, ManagedSettings = new ManagedSettings { Permissions = new ManagedSettingsPermissions @@ -3352,6 +3424,7 @@ public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() var request = Assert.Single(server.Requests, request => request.Method == "session.create"); Assert.True(request.Params.GetProperty("enforceManagedModelDefaults").GetBoolean()); Assert.False(request.Params.TryGetProperty("enableManagedSettings", out _)); + Assert.True(request.Params.GetProperty("interactiveUser").GetBoolean()); var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); Assert.Equal("disable", permissions.GetProperty("disableBypassPermissionsMode").GetString()); Assert.True(permissions.GetProperty("disableAssistedPermissionsMode").GetBoolean()); @@ -3616,6 +3689,7 @@ public async Task CreateSessionAsync_Omits_ManagedSettings_When_Unset() var request = Assert.Single(server.Requests, request => request.Method == "session.create"); Assert.False(request.Params.TryGetProperty("managedSettings", out _)); + Assert.False(request.Params.TryGetProperty("interactiveUser", out _)); } [Fact] @@ -3627,6 +3701,7 @@ public async Task ResumeSessionAsync_Serializes_ManagedSettings_Permissions() await using var session = await client.ResumeSessionAsync("session-managed", new ResumeSessionConfig { EnforceManagedModelDefaults = true, + InteractiveUser = true, ManagedSettings = new ManagedSettings { Permissions = new ManagedSettingsPermissions @@ -3640,10 +3715,53 @@ public async Task ResumeSessionAsync_Serializes_ManagedSettings_Permissions() var request = Assert.Single(server.Requests, request => request.Method == "session.resume"); Assert.True(request.Params.GetProperty("enforceManagedModelDefaults").GetBoolean()); + Assert.True(request.Params.GetProperty("interactiveUser").GetBoolean()); var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); } + [Fact] + public async Task ResumeSessionAsync_Omits_InteractiveUser_When_Unset() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + + await using var session = await client.ResumeSessionAsync("session-no-interactive", new ResumeSessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + OnEvent = _ => { } + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.resume"); + Assert.False(request.Params.TryGetProperty("interactiveUser", out _)); + } + + [Fact] + public async Task CreateAndResumeSessionAsync_Serialize_Explicit_InteractiveUser_False() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var created = await client.CreateSessionAsync(new SessionConfig + { + InteractiveUser = false, + OnPermissionRequest = PermissionHandler.ApproveAll + }); + await using var resumed = await client.ResumeSessionAsync("session-declined-interactive", new ResumeSessionConfig + { + InteractiveUser = false, + OnPermissionRequest = PermissionHandler.ApproveAll, + OnEvent = _ => { } + }); + + foreach (var method in new[] { "session.create", "session.resume" }) + { + var request = Assert.Single(server.Requests, request => request.Method == method); + Assert.Equal(JsonValueKind.False, request.Params.GetProperty("interactiveUser").ValueKind); + } + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index 3ae4c0b009..b473fe7dab 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -665,6 +665,32 @@ public void ResumeSessionConfig_Clone_PreservesEnableOnDemandInstructionDiscover Assert.Null(clone.EnableOnDemandInstructionDiscovery); } + [Fact] + public void SessionConfig_Clone_CopiesEnableGitHubCacheStore() + { + var original = new SessionConfig + { + EnableGitHubCacheStore = true, + }; + + var clone = original.Clone(); + + Assert.True(clone.EnableGitHubCacheStore); + } + + [Fact] + public void ResumeSessionConfig_Clone_CopiesEnableGitHubCacheStore() + { + var original = new ResumeSessionConfig + { + EnableGitHubCacheStore = false, + }; + + var clone = original.Clone(); + + Assert.False(clone.EnableGitHubCacheStore); + } + [Fact] public void SessionConfig_Clone_CopiesMcpOAuthTokenStorage() { 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/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/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index ef41cfa4aa..085e6dfd2c 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -1105,6 +1105,42 @@ public void AgentStopHookInput_DeserializesWireFields_WithSdkOptions() Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1700000000000), input.Timestamp); } + [Theory] + [InlineData("""{"name":"Error","message":"model timeout","stack":"Error: model timeout"}""", "model timeout")] + [InlineData("\"boom\"", "boom")] + [InlineData("""{"code":7}""", """{"code":7}""")] + public void ErrorOccurredHookInput_DecodesCliErrorShapes(string error, string expected) + { + var options = GetSerializerOptions(); + var input = JsonSerializer.Deserialize( + $$"""{"sessionId":"session-1","timestamp":1700000000000,"cwd":"/repo","error":{{error}},"errorContext":"model_call","recoverable":true}""", + options); + + Assert.NotNull(input); + Assert.Equal(expected, input.Error); + Assert.Equal("model_call", input.ErrorContext); + Assert.True(input.Recoverable); + } + + [Theory] + [InlineData("""{"message":"model timeout"}""", "model timeout")] + [InlineData("\"boom\"", "boom")] + [InlineData("null", null)] + [InlineData(null, null)] + [InlineData("""{"code":7}""", """{"code":7}""")] + public void SessionEndHookInput_DecodesCliErrorShapes(string? error, string? expected) + { + var options = GetSerializerOptions(); + var errorField = error is null ? string.Empty : $",\"error\":{error}"; + var input = JsonSerializer.Deserialize( + $$"""{"sessionId":"session-1","timestamp":1700000000000,"cwd":"/repo","reason":"error"{{errorField}}}""", + options); + + Assert.NotNull(input); + Assert.Equal(expected, input.Error); + Assert.Equal("error", input.Reason); + } + [Fact] public void AgentStopHookOutput_SerializesBlockDecision_WithSdkOptions() { diff --git a/go/README.md b/go/README.md index fe297f069b..7e9d78b71f 100644 --- a/go/README.md +++ b/go/README.md @@ -115,6 +115,13 @@ tool name is `-`. For `AvailableTools` and `mcp:-`. For `CustomAgents[].Tools` and `DefaultAgent.ExcludedTools`, use `-` directly. +## Untyped JSON numbers + +Only `ContentExclusionValidatePoliciesResult` decodes untyped JSON numbers as `json.Number` to preserve policy values without precision loss. +Existing RPC results, requests, callbacks, and session events keep their existing numeric representation, including `float64` for untyped numbers. +Typed numeric fields keep their declared Go types. +Use `json.Number.Int64()` or `json.Number.Float64()` when you need a numeric conversion, and handle the returned error. + ## JSON-RPC errors Use `errors.As` to inspect a runtime error without parsing its message, including @@ -383,10 +390,12 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec - `InfiniteSessions` (\*InfiniteSessionConfig): Automatic context compaction configuration - `WorkingDirectory` (string): Working directory for the session (default: runtime process working directory) - `EnableSessionStore` (\*bool): Enables the cross-session store for search and retrieval across sessions. When unset in `ModeCopilotCli`, the runtime default applies (enabled). In `ModeEmpty`, defaults to disabled. +- `EnableGitHubCacheStore` (\*bool): Requests the read-only GitHub cache store (`github-cache-store` capability) on create or resume. The runtime registers the `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is also on. Defaults to disabled in every mode. - `GitHubTokenProvider` (GitHubTokenProvider): Acquires session-scoped GitHub tokens on demand. Return `GitHubTokenResult` with a positive `ExpiresIn` value (production GitHub tokens typically use `8 * 60 * 60` seconds), or `GitHubTokenCancelled`. Cannot be combined with `GitHubToken`. - `OnPermissionRequest` (PermissionHandlerFunc): Optional handler called before each tool execution to approve or deny it. When nil, permission requests are emitted as events and left pending for manual resolution. `copilot.PermissionHandler.ApproveAll` approves requests when managed settings are disabled and returns an error when `EnableManagedSettings` is true. Custom handlers can inspect `RequiresManagedApproval()` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` (UserInputHandler): Handler for legacy question-and-answer requests from the agent. Enables the legacy `ask_user` tool. See [User Input Requests](#user-input-requests) section. - `AskUserVariant` (AskUserVariant): Selects the model-facing shape of the `ask_user` tool. The zero value preserves legacy behavior; use `AskUserVariantElicitation` with `OnElicitationRequest`. +- `InteractiveUser` (\*bool): Declares that a person types this session's messages, as in an editor chat panel. Enables the runtime's user-message analysis telemetry for user-authored messages; automated messages stay excluded. Nil or false keeps the SDK default of off. Ignored for cloud sessions. - `Hooks` (\*SessionHooks): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. - `Commands` ([]CommandDefinition): Slash-commands registered for this session. See [Commands](#commands) section. - `OnElicitationRequest` (ElicitationHandler): Handler for elicitation requests from the server. See [Elicitation Requests](#elicitation-requests-serverclient) section. @@ -401,6 +410,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec - `Commands` ([]CommandDefinition): Slash-commands. See [Commands](#commands) section. - `OnElicitationRequest` (ElicitationHandler): Elicitation handler. See [Elicitation Requests](#elicitation-requests-serverclient) section. - `AskUserVariant` (AskUserVariant): Selects the model-facing shape of the `ask_user` tool on cold resume. Re-supply `AskUserVariantElicitation` with `OnElicitationRequest`; the zero value preserves legacy behavior. +- `InteractiveUser` (\*bool): Re-supplies the interactive-user declaration on cold resume. It is ignored when resuming a session that is already active. - `GitHubTokenProvider` (GitHubTokenProvider): Replaces the session-scoped token provider when resuming. Cannot be combined with `GitHubToken`. ```go diff --git a/go/client.go b/go/client.go index bc41bc4c44..09695d5471 100644 --- a/go/client.go +++ b/go/client.go @@ -932,6 +932,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.EnableFileHooks = config.EnableFileHooks req.EnableHostGitOperations = config.EnableHostGitOperations req.EnableSessionStore = config.EnableSessionStore + req.EnableGitHubCacheStore = config.EnableGitHubCacheStore req.EnableSkills = config.EnableSkills if config.SkillProvider != nil { req.HasSkillProvider = Bool(true) @@ -975,6 +976,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 @@ -1003,6 +1006,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses } req.EnableManagedSettings = config.EnableManagedSettings req.EnforceManagedModelDefaults = config.EnforceManagedModelDefaults + req.InteractiveUser = config.InteractiveUser req.ManagedSettings = config.ManagedSettings if len(config.Commands) > 0 { @@ -1079,6 +1083,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: @@ -1438,6 +1443,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.EnableFileHooks = config.EnableFileHooks req.EnableHostGitOperations = config.EnableHostGitOperations req.EnableSessionStore = config.EnableSessionStore + req.EnableGitHubCacheStore = config.EnableGitHubCacheStore req.EnableSkills = config.EnableSkills if config.SkillProvider != nil { req.HasSkillProvider = Bool(true) @@ -1456,6 +1462,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 @@ -1484,6 +1492,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, } req.EnableManagedSettings = config.EnableManagedSettings req.EnforceManagedModelDefaults = config.EnforceManagedModelDefaults + req.InteractiveUser = config.InteractiveUser req.ManagedSettings = config.ManagedSettings if config.OnPermissionRequest != nil { req.RequestPermission = Bool(true) diff --git a/go/client_test.go b/go/client_test.go index 2283385b33..bf579c5d6c 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -796,6 +796,73 @@ func TestClient_ForwardsCapiOptionsToSessionRequests(t *testing.T) { } } +func TestClient_ForwardsInteractiveUserToSessionRequests(t *testing.T) { + tests := []struct { + name string + value *bool + }{ + {"true", Bool(true)}, + {"false", Bool(false)}, + {"omitted", nil}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + + createParams := make(chan json.RawMessage, 1) + server.SetRequestHandler("session.create", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + createParams <- append(json.RawMessage(nil), params...) + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `","workspacePath":"/workspace"}`), nil + }) + + _, err := client.CreateSession(t.Context(), &SessionConfig{InteractiveUser: tt.value}) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + assertOptionalBoolJSONField(t, <-createParams, "interactiveUser", tt.value) + + resumeParams := make(chan json.RawMessage, 1) + server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + resumeParams <- append(json.RawMessage(nil), params...) + return []byte(`{"sessionId":"interactive-resume","workspacePath":"/workspace"}`), nil + }) + + _, err = client.ResumeSessionWithOptions(t.Context(), "interactive-resume", &ResumeSessionConfig{InteractiveUser: tt.value}) + if err != nil { + t.Fatalf("ResumeSessionWithOptions failed: %v", err) + } + assertOptionalBoolJSONField(t, <-resumeParams, "interactiveUser", tt.value) + }) + } +} + +func assertOptionalBoolJSONField(t *testing.T, raw json.RawMessage, field string, want *bool) { + t.Helper() + + var params map[string]any + if err := json.Unmarshal(raw, ¶ms); err != nil { + t.Fatal(err) + } + got, present := params[field] + if want == nil { + if present { + t.Fatalf("%s = %v, want omitted", field, got) + } + return + } + if got != *want { + t.Fatalf("%s = %v, want %v", field, got, *want) + } +} + func TestClient_RefreshCustomInstructionsIsCreateOnly(t *testing.T) { if _, ok := reflect.TypeOf(ResumeSessionConfig{}).FieldByName("RefreshCustomInstructions"); ok { t.Fatal("ResumeSessionConfig must not expose RefreshCustomInstructions") @@ -1150,6 +1217,68 @@ func assertAdditionalDirectories(t *testing.T, params json.RawMessage, want []st } } +func TestClient_ForwardsGitHubCacheStoreToSessionRequests(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + + createParams := make(chan json.RawMessage, 2) + server.SetRequestHandler("session.create", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + createParams <- append(json.RawMessage(nil), params...) + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `","workspacePath":"/workspace"}`), nil + }) + resumeParams := make(chan json.RawMessage, 2) + server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + resumeParams <- append(json.RawMessage(nil), params...) + return []byte(`{"sessionId":"` + sessionIDFromParams(t, params) + `","workspacePath":"/workspace"}`), nil + }) + + if _, err := client.CreateSession(t.Context(), &SessionConfig{EnableGitHubCacheStore: Bool(true)}); err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + assertGitHubCacheStoreParam(t, <-createParams, Bool(true)) + if _, err := client.ResumeSessionWithOptions( + t.Context(), + "resumed-cache-store", + &ResumeSessionConfig{EnableGitHubCacheStore: Bool(false)}, + ); err != nil { + t.Fatalf("ResumeSessionWithOptions failed: %v", err) + } + assertGitHubCacheStoreParam(t, <-resumeParams, Bool(false)) + + if _, err := client.CreateSession(t.Context(), &SessionConfig{}); err != nil { + t.Fatalf("CreateSession without option failed: %v", err) + } + assertGitHubCacheStoreParam(t, <-createParams, nil) + if _, err := client.ResumeSessionWithOptions(t.Context(), "resumed-cache-store-unset", &ResumeSessionConfig{}); err != nil { + t.Fatalf("ResumeSessionWithOptions without option failed: %v", err) + } + assertGitHubCacheStoreParam(t, <-resumeParams, nil) +} + +func assertGitHubCacheStoreParam(t *testing.T, params json.RawMessage, want *bool) { + t.Helper() + var payload map[string]any + if err := json.Unmarshal(params, &payload); err != nil { + t.Fatalf("failed to decode request params: %v", err) + } + got, present := payload["enableGitHubCacheStore"] + if want == nil { + if present { + t.Fatalf("enableGitHubCacheStore = %v, want omitted", got) + } + return + } + if got != *want { + t.Fatalf("enableGitHubCacheStore = %v (present=%v), want %v", got, present, *want) + } +} + func TestClient_ForwardsCanvasProviderToSessionRequests(t *testing.T) { rpcClient, server, _ := newRuntimeShutdownRpcPair(t) t.Cleanup(server.Stop) @@ -2312,6 +2441,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/hook_identity_test.go b/go/hook_identity_test.go new file mode 100644 index 0000000000..6667fb00c5 --- /dev/null +++ b/go/hook_identity_test.go @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot + +import ( + "encoding/json" + "reflect" + "testing" +) + +func TestHookIdentityTypedDispatch(t *testing.T) { + session, cleanup := newTestSession() + defer cleanup() + session.SessionID = "callback-owner" + var captured any + capture := func(input any, invocation HookInvocation) { + captured = input + if invocation.SessionID != "callback-owner" { + t.Fatalf("callback routing changed: %+v", invocation) + } + } + session.registerHooks(&SessionHooks{ + OnPreToolUse: func(i PreToolUseHookInput, c HookInvocation) (*PreToolUseHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnPreMCPToolCall: func(i PreMCPToolCallHookInput, c HookInvocation) (*PreMCPToolCallHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnPostToolUse: func(i PostToolUseHookInput, c HookInvocation) (*PostToolUseHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnPostToolUseFailure: func(i PostToolUseFailureHookInput, c HookInvocation) (*PostToolUseFailureHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnUserPromptSubmitted: func(i UserPromptSubmittedHookInput, c HookInvocation) (*UserPromptSubmittedHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnUserPromptTransformed: func(i UserPromptTransformedHookInput, c HookInvocation) (*UserPromptTransformedHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnSessionStart: func(i SessionStartHookInput, c HookInvocation) (*SessionStartHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnSessionEnd: func(i SessionEndHookInput, c HookInvocation) (*SessionEndHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnErrorOccurred: func(i ErrorOccurredHookInput, c HookInvocation) (*ErrorOccurredHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnAgentStop: func(i AgentStopHookInput, c HookInvocation) (*AgentStopHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnSubagentStart: func(i SubagentStartHookInput, c HookInvocation) (*SubagentStartHookOutput, error) { + capture(i, c) + return nil, nil + }, + OnSubagentStop: func(i SubagentStopHookInput, c HookInvocation) (*SubagentStopHookOutput, error) { + capture(i, c) + return nil, nil + }, + }) + for _, hook := range []string{"preToolUse", "preMcpToolCall", "postToolUse", "postToolUseFailure", "userPromptSubmitted", "userPromptTransformed", "sessionStart", "sessionEnd", "errorOccurred", "agentStop", "subagentStart", "subagentStop"} { + for _, enriched := range []bool{true, false} { + t.Run(hook+map[bool]string{true: "/child", false: "/old-root"}[enriched], func(t *testing.T) { + input := map[string]any{"sessionId": "reported-session", "timestamp": 1, "cwd": "work"} + if enriched { + input["agentId"], input["agentType"], input["parentAgentId"] = "child-actor", "explore", "parent-actor" + } + raw, err := json.Marshal(input) + if err != nil { + t.Fatal(err) + } + captured = nil + if _, err := session.handleHooksInvoke(hook, raw); err != nil { + t.Fatal(err) + } + value := reflect.ValueOf(captured) + for field, expected := range map[string]string{"AgentID": "child-actor", "AgentType": "explore", "ParentAgentID": "parent-actor"} { + if !enriched { + expected = "" + } + actual := value.FieldByName(field) + if !actual.IsValid() || actual.Kind() != reflect.String || actual.String() != expected { + t.Errorf("%s: actual=%v expected=%q input=%+v", field, actual, expected, captured) + } + } + if value.FieldByName("SessionID").String() != "reported-session" { + t.Errorf("report ID changed: %+v", captured) + } + }) + } + } +} 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/callback_numbers_test.go b/go/rpc/callback_numbers_test.go new file mode 100644 index 0000000000..1f3fae65df --- /dev/null +++ b/go/rpc/callback_numbers_test.go @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc_test + +import ( + "encoding/json" + "testing" + + "github.com/github/copilot-sdk/go/rpc" +) + +func TestCanvasCallbackNumbersRetainFloat64(t *testing.T) { + var opened rpc.CanvasProviderOpenRequest + if err := json.Unmarshal([]byte(`{"extensionId":"counter","canvasId":"counter","instanceId":"panel","sessionId":"session","input":{"startValue":3}}`), &opened); err != nil { + t.Fatal(err) + } + var action rpc.CanvasProviderInvokeActionRequest + if err := json.Unmarshal([]byte(`{"extensionId":"counter","canvasId":"counter","instanceId":"panel","sessionId":"session","actionName":"increment","input":{"amount":2}}`), &action); err != nil { + t.Fatal(err) + } + number := func(input any, name string) float64 { + t.Helper() + value := input.(map[string]any)[name] + number, ok := value.(float64) + if !ok { + t.Fatalf("%s: expected float64, got %T (%v)", name, value, value) + } + return number + } + if got := number(opened.Input, "startValue") + number(action.Input, "amount"); got != 5 { + t.Fatalf("counter result: got %v, want 5", got) + } +} diff --git a/go/rpc/content_exclusion_test.go b/go/rpc/content_exclusion_test.go new file mode 100644 index 0000000000..63dd00b120 --- /dev/null +++ b/go/rpc/content_exclusion_test.go @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc_test + +import ( + "bytes" + "encoding/json" + "reflect" + "testing" + + "github.com/github/copilot-sdk/go/rpc" +) + +func TestValidatedPoliciesPreserveJSONExtensions(t *testing.T) { + for _, input := range []string{ + `{"policies":[]}`, + `{"policies":[{"rules":[{"paths":["src/**"],"source":{"name":"Security","type":"Organization"},"futureRuleField":{"items":[true,null,12.5]}}],"last_updated_at":1720000000,"scope":"all","futurePolicyField":{"nullable":null}}]}`, + `{"policies":[{"rules":[{"paths":["src/**"],"source":{"name":"Security","type":"Organization"},"futureRuleField":9007199254740993}],"last_updated_at":1720000000,"scope":"all","futurePolicyField":9007199254740993}]}`, + } { + t.Run(input, func(t *testing.T) { + var decoded rpc.ContentExclusionValidatePoliciesResult + 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 + expectedDecoder := json.NewDecoder(bytes.NewBufferString(input)) + expectedDecoder.UseNumber() + if err := expectedDecoder.Decode(&expected); err != nil { + t.Fatal(err) + } + actualDecoder := json.NewDecoder(bytes.NewReader(output)) + actualDecoder.UseNumber() + if err := actualDecoder.Decode(&actual); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("policy fields changed:\nactual: %s\nexpected: %s", output, input) + } + }) + } +} 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/result_numbers_test.go b/go/rpc/result_numbers_test.go new file mode 100644 index 0000000000..40b8803146 --- /dev/null +++ b/go/rpc/result_numbers_test.go @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc_test + +import ( + "encoding/json" + "testing" + + "github.com/github/copilot-sdk/go/rpc" +) + +func TestExistingRPCResultNumbersRetainFloat64(t *testing.T) { + var settings rpc.UserSettingsGetResult + var models rpc.ModelList + var prompt rpc.MCPPrompt + var workflow rpc.WorkflowRunResult + var canvas rpc.CanvasActionInvokeResult + for _, test := range []struct { + name string + input string + result any + value func() any + }{ + { + name: "user setting value", + input: `{"settings":{"maxSessionCount":{"value":42,"default":10,"isDefault":false}}}`, + result: &settings, + value: func() any { return settings.Settings["maxSessionCount"].Value }, + }, + { + name: "user setting default", + input: `{"settings":{"maxSessionCount":{"value":10,"default":42,"isDefault":false}}}`, + result: &settings, + value: func() any { return settings.Settings["maxSessionCount"].Default }, + }, + { + name: "model metadata", + input: `{"models":[{"id":"model","name":"Model","capabilities":{},"metadata":{"revision":42}}]}`, + result: &models, + value: func() any { return models.Models[0].Metadata["revision"] }, + }, + { + name: "MCP prompt metadata", + input: `{"name":"prompt","_meta":{"revision":42}}`, + result: &prompt, + value: func() any { return prompt.Meta["revision"] }, + }, + { + name: "workflow result", + input: `{"runId":"run","status":"completed","result":42}`, + result: &workflow, + value: func() any { return workflow.Result }, + }, + { + name: "canvas action result", + input: `{"result":42}`, + result: &canvas, + value: func() any { return canvas.Result }, + }, + } { + t.Run(test.name, func(t *testing.T) { + if err := json.Unmarshal([]byte(test.input), test.result); err != nil { + t.Fatal(err) + } + value := test.value() + number, ok := value.(float64) + if !ok || number != 42 { + t.Fatalf("expected float64(42), got %T (%v)", value, value) + } + }) + } +} 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 fd534106e1..33cd6e0cbb 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 @@ -226,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. @@ -506,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"` @@ -639,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"` @@ -2092,13 +2184,107 @@ type CatalogAgentPluginCandidateProvenance struct { // Host of the catalog authority that advertised the reference, without path, query, or // credentials. Inert untrusted data. Authority string `json:"authority"` - // Canonical Agent Plugin media type. + // Media type naming the plugin's format. MediaType CatalogAgentPluginMediaType `json:"mediaType"` // ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a // retrieval or validation timestamp. ObservedAt string `json:"observedAt"` } +// What installing an Agent Plugin would do, for the user to review and confirm. +// Provisional: the catalogue install data it is built from is not final. +// Experimental: CatalogAgentPluginInstallPlan is part of an experimental API and may change +// or be removed. +type CatalogAgentPluginInstallPlan struct { + // Canvas IDs the plugin contributes. Non-empty exactly when components include `canvas`. + CanvasIDs []string `json:"canvasIds"` + // Exact full commit ID to install, in lowercase hex. Always present when the plan has a + // repository source; a plan with a marketplace source carries at least one of version and + // commit. + Commit *string `json:"commit,omitempty"` + // Components the plugin contributes, in canonical order. + Components []CatalogAgentPluginComponent `json:"components"` + // Connectors the plugin depends on. + ConnectorDependencies []string `json:"connectorDependencies"` + // Extension IDs the plugin contributes. Non-empty exactly when components include + // `extension`. + ExtensionIDs []string `json:"extensionIds"` + // MCP servers the plugin depends on but does not bundle. + MCPDependencies []string `json:"mcpDependencies"` + // The plugin's name, as named in its marketplace or manifest. + PluginName string `json:"pluginName"` + // Every valid install source, in preference order: materialization, then marketplace, then + // repository. Each kind appears at most once. + Sources []CatalogAgentPluginInstallSource `json:"sources"` + // Exact plugin version to install. A plan with a marketplace source carries at least one of + // version and commit. + Version *string `json:"version,omitempty"` +} + +// One place an Agent Plugin can install from: Agent Finder's materialisation, a +// marketplace, or a GitHub repository. Each variant carries only its own fields. +// Experimental: CatalogAgentPluginInstallSource is part of an experimental API and may +// change or be removed. +type CatalogAgentPluginInstallSource interface { + catalogAgentPluginInstallSource() + Kind() CatalogAgentPluginInstallSourceKind +} + +type RawCatalogAgentPluginInstallSourceData struct { + Discriminator CatalogAgentPluginInstallSourceKind + Raw json.RawMessage +} + +func (RawCatalogAgentPluginInstallSourceData) catalogAgentPluginInstallSource() {} +func (r RawCatalogAgentPluginInstallSourceData) Kind() CatalogAgentPluginInstallSourceKind { + return r.Discriminator +} + +// A plugin marketplace named by verified catalogue install data, installed as +// `pluginName@name`. +// Experimental: CatalogAgentPluginMarketplaceSource is part of an experimental API and may +// change or be removed. +type CatalogAgentPluginMarketplaceSource struct { + // Marketplace name. + Name string `json:"name"` + // Canonical lowercase GitHub owner/repository hosting the marketplace. + Repository string `json:"repository"` +} + +func (CatalogAgentPluginMarketplaceSource) catalogAgentPluginInstallSource() {} +func (CatalogAgentPluginMarketplaceSource) Kind() CatalogAgentPluginInstallSourceKind { + return CatalogAgentPluginInstallSourceKindMarketplace +} + +// Agent Finder's verified materialisation of the plugin. Present only when the candidate +// carries a complete materialisation reference; the reference itself stays runtime-private. +// Experimental: CatalogAgentPluginMaterializationSource is part of an experimental API and +// may change or be removed. +type CatalogAgentPluginMaterializationSource struct { +} + +func (CatalogAgentPluginMaterializationSource) catalogAgentPluginInstallSource() {} +func (CatalogAgentPluginMaterializationSource) Kind() CatalogAgentPluginInstallSourceKind { + return CatalogAgentPluginInstallSourceKindMaterialization +} + +// A GitHub repository holding the plugin, installed directly as `owner/repo[:path]` at the +// plan's commit, without a marketplace. +// Experimental: CatalogAgentPluginRepositorySource is part of an experimental API and may +// change or be removed. +type CatalogAgentPluginRepositorySource struct { + // Relative POSIX path of the plugin directory in the repository. Omitted when the plugin is + // at the repository root. + Path *string `json:"path,omitempty"` + // Canonical lowercase GitHub owner/repository holding the plugin. + Repository string `json:"repository"` +} + +func (CatalogAgentPluginRepositorySource) catalogAgentPluginInstallSource() {} +func (CatalogAgentPluginRepositorySource) Kind() CatalogAgentPluginInstallSourceKind { + return CatalogAgentPluginInstallSourceKindRepository +} + // 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. @@ -2153,7 +2339,14 @@ type CatalogAgentPluginCandidate struct { // Validated, normalised catalogue resource URN. This identity comes only from the catalog // identifier and is never inferred from display text or installed-plugin state. Identity string `json:"identity"` - // Canonical Agent Plugin media type. + // Whether this plugin can be installed in the selected session. Present only when the + // caller requires the agent-plugin-confirmed-installation capability. + Installability *CatalogAgentPluginInstallability `json:"installability,omitempty"` + // Reviewable install plan built only from verified catalogue install data. Present exactly + // when installability is `installable`; the runtime never derives it from the repository + // source, display text, or identity. + InstallPlan *CatalogAgentPluginInstallPlan `json:"installPlan,omitempty"` + // Media type naming the plugin's format. MediaType CatalogAgentPluginMediaType `json:"mediaType"` // Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw // data, or content digest. @@ -2326,6 +2519,28 @@ type CatalogClientContract struct { RequiredCapabilities []string `json:"requiredCapabilities"` } +// An Agent Finder materialisation reference pinning one exact resource revision. +// +// It carries no origin or URL: the runtime always builds the descriptor URL on its fixed +// Agent +// Finder authority. It carries no display data either; every reviewed field comes from the +// verified descriptor and Skill files. +// Experimental: CatalogMaterializationReference is part of an experimental API and may +// change or be removed. +type CatalogMaterializationReference struct { + // Expected `sha256:` digest of the descriptor's bundle manifest. + BundleDigest string `json:"bundleDigest"` + // Expected `sha256:` digest of the canonical descriptor. + DescriptorDigest string `json:"descriptorDigest"` + // Agent Finder resource identifier, a positive decimal integer. + ResourceID string `json:"resourceId"` + // Agent Finder materialisation revision identifier, a positive decimal integer. This is not + // a Git commit. + RevisionID string `json:"revisionId"` + // Materialisation descriptor schema version, currently `1.0.0`. + SchemaVersion string `json:"schemaVersion"` +} + // 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. @@ -3686,6 +3901,26 @@ type ContentExclusionPathCheck struct { Path string `json:"path"` } +// Candidate content-exclusion policies to validate, as the raw JSON text a host received. +// The text is supplied unparsed so that a syntax error and a schema violation are both +// reported by the runtime's own validator rather than by the caller's JSON parser. +// Experimental: ContentExclusionValidatePoliciesRequest is part of an experimental API and +// may change or be removed. +type ContentExclusionValidatePoliciesRequest struct { + // Unparsed JSON text expected to hold an array of content-exclusion policies. + PoliciesJSON string `json:"policiesJson"` +} + +// The accepted policies, parsed and normalized. A rejected input is reported as an error +// rather than in this result. +// Experimental: ContentExclusionValidatePoliciesResult is part of an experimental API and +// may change or be removed. +type ContentExclusionValidatePoliciesResult struct { + // Validated policy objects in input order. Raw JSON preserves every field returned by the + // runtime's policy parser. + Policies []any `json:"policies"` +} + // A single large message currently in context. // Experimental: ContextHeaviestMessage is part of an experimental API and may change or be // removed. @@ -4954,6 +5189,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. @@ -5019,6 +5374,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. @@ -5145,6 +5553,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"` @@ -5770,6 +6180,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. @@ -6413,6 +6829,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. @@ -6420,6 +6867,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). @@ -6433,6 +6885,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"` @@ -6446,6 +6903,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 @@ -6713,6 +7180,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. @@ -6868,6 +7345,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 { @@ -6930,6 +7432,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. @@ -6943,6 +7590,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. @@ -6959,6 +7680,29 @@ 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. @@ -6969,6 +7713,26 @@ type ManagedSettingsPermissionsEvaluateResult struct { 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. @@ -6980,6 +7744,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 @@ -7031,6 +7842,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 { @@ -7081,6 +7940,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. @@ -7579,6 +8629,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). @@ -8387,6 +9439,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. @@ -9299,6 +10362,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. @@ -9322,6 +10387,8 @@ type MCPRegisterExternalClientRequest struct { // 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"` @@ -9330,6 +10397,8 @@ type MCPRegistryCancelRequest struct { // 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 @@ -9340,6 +10409,8 @@ type MCPRegistryCancelResult struct { // 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. @@ -9350,6 +10421,8 @@ type MCPRegistryRequestIDResult struct { // 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 @@ -9373,6 +10446,8 @@ type MCPRegistrySearchRequest struct { // 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 @@ -9382,6 +10457,7 @@ type MCPRegistrySearchResult struct { // 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`. @@ -9402,6 +10478,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). @@ -9907,6 +10985,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"` @@ -10031,6 +11111,8 @@ type MCPSetEnvValueModeResult struct { // 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. @@ -10048,6 +11130,8 @@ type MCPShouldExcludeGitHubToolsRequest struct { // 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 @@ -10181,6 +11265,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"` @@ -10721,6 +11807,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. @@ -12322,6 +13470,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 @@ -12435,6 +13589,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. @@ -12814,6 +13994,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 { @@ -14658,6 +15863,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. @@ -14671,6 +15970,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 { @@ -14905,6 +16228,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. @@ -14917,6 +16286,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 @@ -15020,6 +16426,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. @@ -15055,6 +16511,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. @@ -16765,6 +18323,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"` @@ -16842,8 +18406,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"` @@ -16882,6 +18446,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"` @@ -16895,8 +18466,20 @@ 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"` + // Declares that a human drives this session's conversation, enabling user-message analysis + // (sentiment, task completion criteria, and task analytics) for user-authored messages. + // Automated messages remain excluded. Defaults to false for SDK sessions. Applied when the + // session is created or loaded from storage; ignored when resuming a session that is + // already active, and for cloud sessions. + InteractiveUser *bool `json:"interactiveUser,omitempty"` // Whether experimental behavior is enabled. IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` // Whether interactive shell sessions are logged. @@ -16972,7 +18555,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"` @@ -18322,6 +19909,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"` @@ -18349,8 +19942,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). @@ -18365,9 +19959,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"` @@ -18387,6 +19982,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"` @@ -18401,6 +20003,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. @@ -18456,7 +20064,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"` @@ -18647,6 +20259,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. @@ -19465,6 +21089,20 @@ type SkillList struct { Skills []Skill `json:"skills"` } +// Side-effect-free planning of one Skill from a materialisation reference rather than a +// search candidate. +// Experimental: SkillPlanInstallFromReferenceRequest is part of an experimental API and may +// change or be removed. +type SkillPlanInstallFromReferenceRequest struct { + // Required authenticated bound catalogue, skill-confirmed-installation and + // skill-reference-installation capabilities. + Contract CatalogClientContract `json:"contract"` + // Existing local session attached to this connection. + PolicySessionID string `json:"policySessionId"` + // Materialisation reference to verify and plan. + Reference CatalogMaterializationReference `json:"reference"` +} + // Side-effect-free planning of one verified Agent Finder Skill candidate. // Experimental: SkillPlanInstallRequest is part of an experimental API and may change or be // removed. @@ -19600,7 +21238,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"` } @@ -19638,6 +21279,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). @@ -21516,6 +23162,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 @@ -21526,6 +23279,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 @@ -21540,6 +23705,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. @@ -23327,16 +25599,104 @@ const ( CatalogAgentPluginCompatibilityTagGitHubCopilot CatalogAgentPluginCompatibilityTag = "github-copilot" ) -// Canonical Agent Plugin media type +// A kind of component an Agent Plugin contributes +// Experimental: CatalogAgentPluginComponent is part of an experimental API and may change +// or be removed. +type CatalogAgentPluginComponent string + +const ( + // Custom agents. + CatalogAgentPluginComponentAgent CatalogAgentPluginComponent = "agent" + // Canvases. + CatalogAgentPluginComponentCanvas CatalogAgentPluginComponent = "canvas" + // Slash commands. + CatalogAgentPluginComponentCommand CatalogAgentPluginComponent = "command" + // Extensions. + CatalogAgentPluginComponentExtension CatalogAgentPluginComponent = "extension" + // Hooks. + CatalogAgentPluginComponentHook CatalogAgentPluginComponent = "hook" + // Bundled LSP servers. + CatalogAgentPluginComponentLspServer CatalogAgentPluginComponent = "lsp-server" + // Bundled MCP servers. + CatalogAgentPluginComponentMCPServer CatalogAgentPluginComponent = "mcp-server" + // Skills. + CatalogAgentPluginComponentSkill CatalogAgentPluginComponent = "skill" +) + +// Typed installability state for an Agent Plugin candidate +// Experimental: CatalogAgentPluginInstallability is part of an experimental API and may +// change or be removed. +type CatalogAgentPluginInstallability string + +const ( + // Plugin installation is understood but disabled for the selected session. + CatalogAgentPluginInstallabilityFeatureDisabled CatalogAgentPluginInstallability = "feature-disabled" + // The candidate carries complete, verified install data and the selected session may + // install it. Its install plan is present. + CatalogAgentPluginInstallabilityInstallable CatalogAgentPluginInstallability = "installable" + // The catalogue supplied no complete, verified install data, so no install target is + // offered. + CatalogAgentPluginInstallabilityInstallDataUnavailable CatalogAgentPluginInstallability = "install-data-unavailable" + // Reserved for plugin formats this runtime cannot install. Every format surfaced today + // installs through a marketplace. + CatalogAgentPluginInstallabilityNotInstallableKind CatalogAgentPluginInstallability = "not-installable-kind" +) + +// Kind discriminator for CatalogAgentPluginInstallSource. +type CatalogAgentPluginInstallSourceKind string + +const ( + CatalogAgentPluginInstallSourceKindMarketplace CatalogAgentPluginInstallSourceKind = "marketplace" + CatalogAgentPluginInstallSourceKindMaterialization CatalogAgentPluginInstallSourceKind = "materialization" + CatalogAgentPluginInstallSourceKindRepository CatalogAgentPluginInstallSourceKind = "repository" +) + +// Discriminator for a marketplace install source +// Experimental: CatalogAgentPluginMarketplaceSourceKind is part of an experimental API and +// may change or be removed. +type CatalogAgentPluginMarketplaceSourceKind string + +const ( + // Install from a plugin marketplace. + CatalogAgentPluginMarketplaceSourceKindMarketplace CatalogAgentPluginMarketplaceSourceKind = "marketplace" +) + +// Discriminator for a materialisation install source +// Experimental: CatalogAgentPluginMaterializationSourceKind is part of an experimental API +// and may change or be removed. +type CatalogAgentPluginMaterializationSourceKind string + +const ( + // Install from Agent Finder's verified materialisation. + CatalogAgentPluginMaterializationSourceKindMaterialization CatalogAgentPluginMaterializationSourceKind = "materialization" +) + +// Media type naming the plugin format of an Agent Plugin candidate. Values other than +// `application/vnd.github.copilot-plugin` appear only when the caller requires the +// agent-plugin-formats capability. // Experimental: CatalogAgentPluginMediaType is part of an experimental API and may change // or be removed. type CatalogAgentPluginMediaType string const ( + // A portable Agent Plugin descriptor. + CatalogAgentPluginMediaTypeApplicationAgentPluginJSON CatalogAgentPluginMediaType = "application/agent-plugin+json" + // A Claude plugin descriptor (`.claude-plugin/plugin.json` layout). + CatalogAgentPluginMediaTypeApplicationVndAnthropicClaudePluginJSON CatalogAgentPluginMediaType = "application/vnd.anthropic.claude-plugin+json" // A GitHub Copilot Agent Plugin descriptor. CatalogAgentPluginMediaTypeApplicationVndGitHubCopilotPlugin CatalogAgentPluginMediaType = "application/vnd.github.copilot-plugin" ) +// Discriminator for a repository install source +// Experimental: CatalogAgentPluginRepositorySourceKind is part of an experimental API and +// may change or be removed. +type CatalogAgentPluginRepositorySourceKind string + +const ( + // Install directly from a GitHub repository. + CatalogAgentPluginRepositorySourceKindRepository CatalogAgentPluginRepositorySourceKind = "repository" +) + // Discriminator for an AI skill candidate // Experimental: CatalogAiSkillCandidateKind is part of an experimental API and may change // or be removed. @@ -23420,9 +25780,20 @@ const ( type CatalogCapability string const ( + // Understands `installability` and the reviewable `installPlan` on Agent Plugin candidates. + // Requires agent-plugin-discovery; without it, plugin candidates are unchanged. A plugin is + // installable only with complete, verified catalogue install data, and may still be + // reported as feature-disabled per selected session. + CatalogCapabilityAgentPluginConfirmedInstallation CatalogCapability = "agent-plugin-confirmed-installation" // Understands opt-in `application/vnd.github.copilot-plugin` candidates and their typed // identity, version, source, and compatibility fields. CatalogCapabilityAgentPluginDiscovery CatalogCapability = "agent-plugin-discovery" + // Extends agent-plugin-discovery to Claude plugin + // (`application/vnd.anthropic.claude-plugin+json`) and portable Agent Plugin + // (`application/agent-plugin+json`) candidates, each reporting its format as its media + // type. Requires agent-plugin-discovery; without it, plugin searches return only Copilot + // plugins. + CatalogCapabilityAgentPluginFormats CatalogCapability = "agent-plugin-formats" // Understands `application/ai-skill` candidates as discovery-only and typed non-installable. CatalogCapabilityAiSkillDiscovery CatalogCapability = "ai-skill-discovery" // Requires an eligible credential for the selected GitHub.com account before search egress @@ -23469,6 +25840,11 @@ const ( // installation engine linked; acquisition may still be refused as feature-disabled per // selected session. CatalogCapabilitySkillConfirmedInstallation CatalogCapability = "skill-confirmed-installation" + // Extends skill-confirmed-installation to planning from an Agent Finder materialisation + // reference through skills.planInstallFromReference, and to its distinct reference failure + // reasons. Requires skill-confirmed-installation. Advertised only by runtimes with the + // Skill installation engine linked. + CatalogCapabilitySkillReferenceInstallation CatalogCapability = "skill-reference-installation" // Understands versioned candidate trust snapshots. Protocol-3 callers must require this // capability before the runtime adds the optional snapshot field. CatalogCapabilityTrustSnapshot CatalogCapability = "trust-snapshot" @@ -23604,12 +25980,18 @@ const ( type CatalogMediaType string const ( + // An inert portable Agent Plugin descriptor, surfaced only under the agent-plugin-formats + // capability. + CatalogMediaTypeApplicationAgentPluginJSON CatalogMediaType = "application/agent-plugin+json" // An AI skill card. Representable and searchable, but typed non-installable. CatalogMediaTypeApplicationAiSkill CatalogMediaType = "application/ai-skill" // The current MCP server card media type. CatalogMediaTypeApplicationMCPServerCardJSON CatalogMediaType = "application/mcp-server-card+json" // The legacy MCP server card media type, accepted for compatibility. CatalogMediaTypeApplicationMCPServerJSON CatalogMediaType = "application/mcp-server+json" + // An inert Claude plugin descriptor, surfaced only under the agent-plugin-formats + // capability. + CatalogMediaTypeApplicationVndAnthropicClaudePluginJSON CatalogMediaType = "application/vnd.anthropic.claude-plugin+json" // An inert Agent Plugin descriptor. CatalogMediaTypeApplicationVndGitHubCopilotPlugin CatalogMediaType = "application/vnd.github.copilot-plugin" ) @@ -24426,6 +26808,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. @@ -24839,6 +27247,41 @@ const ( 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. @@ -24865,6 +27308,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 @@ -24888,6 +27371,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. @@ -26698,6 +29219,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, @@ -26765,6 +29288,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 @@ -26867,6 +29402,8 @@ const ( SessionCapabilityCLIDocumentation SessionCapability = "cli-documentation" // SDK elicitation support. SessionCapabilityElicitation SessionCapability = "elicitation" + // Read-only GitHub cache SQL tool (github_sql). + SessionCapabilityGitHubCacheStore SessionCapability = "github-cache-store" // Interactive CLI identity and behavior. SessionCapabilityInteractiveMode SessionCapability = "interactive-mode" // MCP Apps UI passthrough. @@ -27335,6 +29872,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 @@ -27421,8 +29988,17 @@ const ( SkillInstallationFailureReasonConfirmationUnavailable SkillInstallationFailureReason = "confirmation-unavailable" // The verified Skill descriptor failed validation. SkillInstallationFailureReasonDescriptorInvalid SkillInstallationFailureReason = "descriptor-invalid" + // Agent Finder has no materialisation for the referenced resource revision (HTTP 404). + // Returned only to callers that require skill-reference-installation. + SkillInstallationFailureReasonDescriptorNotFound SkillInstallationFailureReason = "descriptor-not-found" // The verified Skill descriptor could not be retrieved safely. SkillInstallationFailureReasonDescriptorUnavailable SkillInstallationFailureReason = "descriptor-unavailable" + // Agent Finder withdrew the referenced resource revision (HTTP 410). Returned only to + // callers that require skill-reference-installation. + SkillInstallationFailureReasonDescriptorWithdrawn SkillInstallationFailureReason = "descriptor-withdrawn" + // The retrieved descriptor does not match the referenced descriptor or bundle digest. + // Returned only to callers that require skill-reference-installation. + SkillInstallationFailureReasonDigestMismatch SkillInstallationFailureReason = "digest-mismatch" // The Skill entrypoint could not be retrieved or verified. SkillInstallationFailureReasonEntrypointUnavailable SkillInstallationFailureReason = "entrypoint-unavailable" // The handle or operation expired. @@ -27434,6 +30010,9 @@ const ( SkillInstallationFailureReasonForeignRuntime SkillInstallationFailureReason = "foreign-runtime" // The candidate handle is not a verified installable Skill candidate. SkillInstallationFailureReasonInvalidCandidate SkillInstallationFailureReason = "invalid-candidate" + // The materialisation reference is malformed or uses an unsupported schema version. + // Returned only to callers that require skill-reference-installation. + SkillInstallationFailureReasonInvalidReference SkillInstallationFailureReason = "invalid-reference" // The request is unsupported or malformed. SkillInstallationFailureReasonInvalidRequest SkillInstallationFailureReason = "invalid-request" // The Skill entrypoint is not a valid Skill. @@ -27464,6 +30043,9 @@ const ( SkillInstallationFailureReasonSearchMismatch SkillInstallationFailureReason = "search-mismatch" // The retained Skill source changed after planning. SkillInstallationFailureReasonSourceChanged SkillInstallationFailureReason = "source-changed" + // The referenced resource is not a Skill. Returned only to callers that require + // skill-reference-installation. + SkillInstallationFailureReasonUnsupportedResource SkillInstallationFailureReason = "unsupported-resource" // A storage operation failed; inspect durable state before retrying. SkillInstallationFailureReasonWriteFailed SkillInstallationFailureReason = "write-failed" // The handle is not the expected Skill handle kind. @@ -27644,6 +30226,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. @@ -27970,6 +30591,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 @@ -28511,6 +31149,39 @@ func (a *ServerConnectorsAPI) Refresh(ctx context.Context, params *ConnectorDisc return &result, nil } +// Experimental: ServerContentExclusionAPI contains experimental APIs that may change or be +// removed. +type ServerContentExclusionAPI serverAPI + +// ValidatePolicies validates candidate content-exclusion policies and returns them +// normalized, without starting a session. This runs the same parser the runtime's own +// policy enforcement uses, so a host cannot accept a policy set the runtime would later +// reject, nor reject one it would have honored. Call it when policies arrive from outside +// the runtime — a command-line flag, a configuration file, a host's own settings — and must +// be checked before a session exists to apply them to. A malformed document fails with a +// message prefixed `Invalid JSON: `; a well-formed document that violates the policy schema +// fails with the offending field paths. +// +// RPC method: contentExclusion.validatePolicies. +// +// Parameters: Candidate content-exclusion policies to validate, as the raw JSON text a host +// received. The text is supplied unparsed so that a syntax error and a schema violation are +// both reported by the runtime's own validator rather than by the caller's JSON parser. +// +// Returns: The accepted policies, parsed and normalized. A rejected input is reported as an +// error rather than in this result. +func (a *ServerContentExclusionAPI) ValidatePolicies(ctx context.Context, params *ContentExclusionValidatePoliciesRequest) (*ContentExclusionValidatePoliciesResult, error) { + raw, err := a.client.Request(ctx, "contentExclusion.validatePolicies", params) + if err != nil { + return nil, err + } + var result ContentExclusionValidatePoliciesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: ServerEnvironmentsAPI contains experimental APIs that may change or be // removed. type ServerEnvironmentsAPI serverAPI @@ -29763,6 +32434,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 @@ -30594,6 +33307,31 @@ func (a *ServerSkillsAPI) PlanInstall(ctx context.Context, params *SkillPlanInst return result, nil } +// PlanInstallFromReference plans installation of the Skill pinned by an Agent Finder +// materialisation reference without writing files. The runtime retrieves the descriptor +// only from its fixed Agent Finder authority and applies the same verification as +// skills.planInstall; the review shows only verified data. Apply the returned plan with +// skills.applyInstall. +// +// RPC method: skills.planInstallFromReference. +// +// Parameters: Side-effect-free planning of one Skill from a materialisation reference +// rather than a search candidate. +// +// Returns: Skill installation management result with the honoured contract, or a typed +// refusal. +func (a *ServerSkillsAPI) PlanInstallFromReference(ctx context.Context, params *SkillPlanInstallFromReferenceRequest) (SkillInstallationManagementResult, error) { + raw, err := a.client.Request(ctx, "skills.planInstallFromReference", params) + if err != nil { + return nil, err + } + result, err := unmarshalSkillInstallationManagementResult(raw) + if err != nil { + return nil, err + } + return result, nil +} + // PlanUninstall 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. // @@ -30860,30 +33598,31 @@ type ServerRPC struct { // Reuse a single struct instead of allocating one for each service on the heap. common serverAPI - Account *ServerAccountAPI - AgentRegistry *ServerAgentRegistryAPI - Agents *ServerAgentsAPI - Catalog *ServerCatalogAPI - Commands *ServerCommandsAPI - Connectors *ServerConnectorsAPI - Environments *ServerEnvironmentsAPI - Extensions *ServerExtensionsAPI - Hooks *ServerHooksAPI - Host *ServerHostAPI - Instructions *ServerInstructionsAPI - LlmInference *ServerLlmInferenceAPI - ManagedSettings *ServerManagedSettingsAPI - MCP *ServerMCPAPI - Models *ServerModelsAPI - Plugins *ServerPluginsAPI - Runtime *ServerRuntimeAPI - Sandbox *ServerSandboxAPI - Secrets *ServerSecretsAPI - SessionFS *ServerSessionFSAPI - Sessions *ServerSessionsAPI - Skills *ServerSkillsAPI - Tools *ServerToolsAPI - User *ServerUserAPI + Account *ServerAccountAPI + AgentRegistry *ServerAgentRegistryAPI + Agents *ServerAgentsAPI + Catalog *ServerCatalogAPI + Commands *ServerCommandsAPI + Connectors *ServerConnectorsAPI + ContentExclusion *ServerContentExclusionAPI + Environments *ServerEnvironmentsAPI + Extensions *ServerExtensionsAPI + Hooks *ServerHooksAPI + Host *ServerHostAPI + Instructions *ServerInstructionsAPI + LlmInference *ServerLlmInferenceAPI + ManagedSettings *ServerManagedSettingsAPI + MCP *ServerMCPAPI + Models *ServerModelsAPI + Plugins *ServerPluginsAPI + Runtime *ServerRuntimeAPI + Sandbox *ServerSandboxAPI + Secrets *ServerSecretsAPI + SessionFS *ServerSessionFSAPI + Sessions *ServerSessionsAPI + Skills *ServerSkillsAPI + Tools *ServerToolsAPI + User *ServerUserAPI } // Ping checks server responsiveness and returns protocol information. @@ -30935,6 +33674,7 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.Catalog = (*ServerCatalogAPI)(&r.common) r.Commands = (*ServerCommandsAPI)(&r.common) r.Connectors = (*ServerConnectorsAPI)(&r.common) + r.ContentExclusion = (*ServerContentExclusionAPI)(&r.common) r.Environments = (*ServerEnvironmentsAPI)(&r.common) r.Extensions = (*ServerExtensionsAPI)(&r.common) r.Hooks = (*ServerHooksAPI)(&r.common) @@ -30960,6 +33700,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 @@ -31098,6 +33937,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 @@ -31508,10 +34436,452 @@ 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 @@ -31629,6 +34999,672 @@ 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 @@ -31876,20 +35912,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 @@ -31924,14 +36647,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 } @@ -35097,6 +39829,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. @@ -35376,6 +40145,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 } @@ -35454,6 +40226,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 } @@ -35466,6 +40241,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 } diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 471e491a87..bb207c0c38 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -4,6 +4,7 @@ package rpc import ( + "bytes" "encoding/json" "errors" ) @@ -1259,6 +1260,123 @@ func (r *BuiltinToolDescriptor) UnmarshalJSON(data []byte) error { return nil } +func unmarshalCatalogAgentPluginInstallSource(data []byte) (CatalogAgentPluginInstallSource, error) { + if string(data) == "null" { + return nil, nil + } + type rawUnion struct { + Kind CatalogAgentPluginInstallSourceKind `json:"kind"` + } + var raw rawUnion + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + switch raw.Kind { + case CatalogAgentPluginInstallSourceKindMarketplace: + var d CatalogAgentPluginMarketplaceSource + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case CatalogAgentPluginInstallSourceKindMaterialization: + var d CatalogAgentPluginMaterializationSource + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case CatalogAgentPluginInstallSourceKindRepository: + var d CatalogAgentPluginRepositorySource + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + default: + return &RawCatalogAgentPluginInstallSourceData{Discriminator: raw.Kind, Raw: data}, nil + } +} + +func (r RawCatalogAgentPluginInstallSourceData) MarshalJSON() ([]byte, error) { + if r.Raw != nil { + return r.Raw, nil + } + return json.Marshal(struct { + Kind CatalogAgentPluginInstallSourceKind `json:"kind"` + }{ + Kind: r.Discriminator, + }) +} + +func (r CatalogAgentPluginMarketplaceSource) MarshalJSON() ([]byte, error) { + type alias CatalogAgentPluginMarketplaceSource + return json.Marshal(struct { + Kind CatalogAgentPluginInstallSourceKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r CatalogAgentPluginMaterializationSource) MarshalJSON() ([]byte, error) { + type alias CatalogAgentPluginMaterializationSource + return json.Marshal(struct { + Kind CatalogAgentPluginInstallSourceKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r CatalogAgentPluginRepositorySource) MarshalJSON() ([]byte, error) { + type alias CatalogAgentPluginRepositorySource + return json.Marshal(struct { + Kind CatalogAgentPluginInstallSourceKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r *CatalogAgentPluginInstallPlan) UnmarshalJSON(data []byte) error { + type rawCatalogAgentPluginInstallPlan struct { + CanvasIDs []string `json:"canvasIds"` + Commit *string `json:"commit,omitempty"` + Components []CatalogAgentPluginComponent `json:"components"` + ConnectorDependencies []string `json:"connectorDependencies"` + ExtensionIDs []string `json:"extensionIds"` + MCPDependencies []string `json:"mcpDependencies"` + PluginName string `json:"pluginName"` + Sources []json.RawMessage `json:"sources"` + Version *string `json:"version,omitempty"` + } + var raw rawCatalogAgentPluginInstallPlan + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + r.CanvasIDs = raw.CanvasIDs + r.Commit = raw.Commit + r.Components = raw.Components + r.ConnectorDependencies = raw.ConnectorDependencies + r.ExtensionIDs = raw.ExtensionIDs + r.MCPDependencies = raw.MCPDependencies + r.PluginName = raw.PluginName + if raw.Sources != nil { + r.Sources = make([]CatalogAgentPluginInstallSource, 0, len(raw.Sources)) + for _, rawItem := range raw.Sources { + value, err := unmarshalCatalogAgentPluginInstallSource(rawItem) + if err != nil { + return err + } + r.Sources = append(r.Sources, value) + } + } + r.Version = raw.Version + return nil +} + func unmarshalCatalogCandidate(data []byte) (CatalogCandidate, error) { if string(data) == "null" { return nil, nil @@ -1642,6 +1760,8 @@ func (r *CatalogAgentPluginCandidate) UnmarshalJSON(data []byte) error { Description *string `json:"description,omitempty"` DisplayName string `json:"displayName"` Identity string `json:"identity"` + Installability *CatalogAgentPluginInstallability `json:"installability,omitempty"` + InstallPlan *CatalogAgentPluginInstallPlan `json:"installPlan,omitempty"` MediaType CatalogAgentPluginMediaType `json:"mediaType"` Provenance CatalogAgentPluginCandidateProvenance `json:"provenance"` Publisher *string `json:"publisher,omitempty"` @@ -1657,6 +1777,8 @@ func (r *CatalogAgentPluginCandidate) UnmarshalJSON(data []byte) error { r.Description = raw.Description r.DisplayName = raw.DisplayName r.Identity = raw.Identity + r.Installability = raw.Installability + r.InstallPlan = raw.InstallPlan r.MediaType = raw.MediaType r.Provenance = raw.Provenance r.Publisher = raw.Publisher @@ -2451,6 +2573,13 @@ func (r ConnectorConnectResultPending) MarshalJSON() ([]byte, error) { }) } +func (r *ContentExclusionValidatePoliciesResult) UnmarshalJSON(data []byte) error { + type wire ContentExclusionValidatePoliciesResult + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.UseNumber() + return decoder.Decode((*wire)(r)) +} + func unmarshalDebugCollectLogsDestination(data []byte) (DebugCollectLogsDestination, error) { if string(data) == "null" { return nil, nil @@ -7721,6 +7850,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,11 +7886,14 @@ 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"` + InteractiveUser *bool `json:"interactiveUser,omitempty"` IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` LogInteractiveShells *bool `json:"logInteractiveShells,omitempty"` LspClientName *string `json:"lspClientName,omitempty"` @@ -7803,6 +7936,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,11 +7978,14 @@ 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.InteractiveUser = raw.InteractiveUser r.IsExperimentalMode = raw.IsExperimentalMode r.LogInteractiveShells = raw.LogInteractiveShells r.LspClientName = raw.LspClientName 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 cef0367976..eeec88c09a 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -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 @@ -668,9 +672,6 @@ func (*SessionContextClearedData) Type() SessionEventType { // Conversation compaction results including success status, metrics, and optional error details type SessionCompactionCompleteData struct { - // Legacy active-workflow reminder retained for replay compatibility - // Internal: ActiveFactorySummary is part of the SDK's internal API surface and is not intended for external use. - ActiveFactorySummary *string `json:"activeFactorySummary,omitempty"` // Authoritative active-workflow reminder appended to the compacted context // Internal: ActiveWorkflowSummary is part of the SDK's internal API surface and is not intended for external use. ActiveWorkflowSummary *string `json:"activeWorkflowSummary,omitempty"` @@ -1048,12 +1049,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 @@ -2465,6 +2470,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 @@ -3163,8 +3172,6 @@ type SubagentStartedData struct { AgentType *string `json:"agentType,omitempty"` // Whether the sub-agent runs synchronously or in the background. ExecutionMode *string `json:"executionMode,omitempty"` - // Legacy root id of the workflow run that spawned this sub-agent. New consumers should use workflowRunId. - FactoryRunID *string `json:"factoryRunId,omitempty"` // Model the sub-agent will run with, when known at start. Model *string `json:"model,omitempty"` // Authority or runtime mechanism responsible for sub-agent model selection, when known at start. @@ -3826,6 +3833,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 diff --git a/go/session_test.go b/go/session_test.go index 38a365fbd6..39eb312021 100644 --- a/go/session_test.go +++ b/go/session_test.go @@ -1985,6 +1985,77 @@ func TestSession_PostToolUseFailureHook(t *testing.T) { }) } +func TestSession_ErrorHooksDecodeCliErrorObject(t *testing.T) { + cases := []struct { + name string + error string + want string + }{ + {"error object", `"error": {"name": "Error", "message": "model timeout", "stack": "Error: model timeout"},`, "model timeout"}, + {"string", `"error": "boom",`, "boom"}, + {"null", `"error": null,`, ""}, + {"absent", ``, ""}, + {"object without a string message", `"error": {"code": 7},`, `{"code":7}`}, + } + for _, tc := range cases { + t.Run("errorOccurred/"+tc.name, func(t *testing.T) { + session, cleanup := newTestSession() + defer cleanup() + + var captured *ErrorOccurredHookInput + session.registerHooks(&SessionHooks{ + OnErrorOccurred: func(input ErrorOccurredHookInput, _ HookInvocation) (*ErrorOccurredHookOutput, error) { + captured = &input + return nil, nil + }, + }) + + raw := json.RawMessage(`{"sessionId": "sess-1", "timestamp": 1700000000, "cwd": "/work", ` + tc.error + + ` "errorContext": "model_call", "recoverable": true}`) + if _, err := session.handleHooksInvoke("errorOccurred", raw); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if captured == nil { + t.Fatal("OnErrorOccurred did not run") + } + if captured.Error != tc.want { + t.Errorf("expected error %q, got %q", tc.want, captured.Error) + } + if captured.ErrorContext != "model_call" || !captured.Recoverable { + t.Errorf("unexpected input: %+v", *captured) + } + }) + + t.Run("sessionEnd/"+tc.name, func(t *testing.T) { + session, cleanup := newTestSession() + defer cleanup() + + var captured *SessionEndHookInput + session.registerHooks(&SessionHooks{ + OnSessionEnd: func(input SessionEndHookInput, _ HookInvocation) (*SessionEndHookOutput, error) { + captured = &input + return nil, nil + }, + }) + + raw := json.RawMessage(`{"sessionId": "sess-1", "timestamp": 1700000000, "cwd": "/work", ` + tc.error + + ` "reason": "error"}`) + if _, err := session.handleHooksInvoke("sessionEnd", raw); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if captured == nil { + t.Fatal("OnSessionEnd did not run") + } + if captured.Error != tc.want { + t.Errorf("expected error %q, got %q", tc.want, captured.Error) + } + if captured.Reason != "error" { + t.Errorf("expected reason 'error', got %q", captured.Reason) + } + }) + } +} + func TestSession_AgentStopHook(t *testing.T) { session, cleanup := newTestSession() defer cleanup() diff --git a/go/types.go b/go/types.go index cdeae3b404..1ef6152946 100644 --- a/go/types.go +++ b/go/types.go @@ -1,6 +1,7 @@ package copilot import ( + "bytes" "context" "encoding/json" "errors" @@ -645,6 +646,9 @@ type AutoModeSwitchRequestHandler func(request AutoModeSwitchRequest, invocation // PreToolUseHookInput is the input for a pre-tool-use hook type PreToolUseHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -689,6 +693,9 @@ type PreToolUseHandler func(input PreToolUseHookInput, invocation HookInvocation // PostToolUseHookInput is the input for a post-tool-use hook type PostToolUseHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -736,6 +743,9 @@ type PostToolUseHandler func(input PostToolUseHookInput, invocation HookInvocati // the failure message from the tool result and passes it as the Error field // (rather than passing the full result object). type PostToolUseFailureHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -781,6 +791,9 @@ type PostToolUseFailureHandler func(input PostToolUseFailureHookInput, invocatio // UserPromptSubmittedHookInput is the input for a user-prompt-submitted hook type UserPromptSubmittedHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -822,6 +835,9 @@ type UserPromptSubmittedHandler func(input UserPromptSubmittedHookInput, invocat // UserPromptTransformedHookInput is the input for a user-prompt-transformed hook. type UserPromptTransformedHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -862,6 +878,9 @@ type UserPromptTransformedHandler func(input UserPromptTransformedHookInput, inv // SessionStartHookInput is the input for a session-start hook type SessionStartHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -903,6 +922,9 @@ type SessionStartHandler func(input SessionStartHookInput, invocation HookInvoca // SessionEndHookInput is the input for a session-end hook type SessionEndHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -920,17 +942,24 @@ func (h SessionEndHookInput) MarshalJSON() ([]byte, error) { }{Timestamp: h.Timestamp.UnixMilli(), alias: alias(h)}) } -// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds. +// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds +// and Error from the CLI's error object (see hookErrorText). func (h *SessionEndHookInput) UnmarshalJSON(data []byte) error { type alias SessionEndHookInput aux := &struct { - Timestamp int64 `json:"timestamp"` + Timestamp int64 `json:"timestamp"` + Error json.RawMessage `json:"error"` *alias }{alias: (*alias)(h)} if err := json.Unmarshal(data, aux); err != nil { return err } h.Timestamp = time.UnixMilli(aux.Timestamp) + errorText, err := hookErrorText(aux.Error) + if err != nil { + return err + } + h.Error = errorText return nil } @@ -946,6 +975,9 @@ type SessionEndHandler func(input SessionEndHookInput, invocation HookInvocation // ErrorOccurredHookInput is the input for an error-occurred hook type ErrorOccurredHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -963,20 +995,62 @@ func (h ErrorOccurredHookInput) MarshalJSON() ([]byte, error) { }{Timestamp: h.Timestamp.UnixMilli(), alias: alias(h)}) } -// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds. +// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds +// and Error from the CLI's error object (see hookErrorText). func (h *ErrorOccurredHookInput) UnmarshalJSON(data []byte) error { type alias ErrorOccurredHookInput aux := &struct { - Timestamp int64 `json:"timestamp"` + Timestamp int64 `json:"timestamp"` + Error json.RawMessage `json:"error"` *alias }{alias: (*alias)(h)} if err := json.Unmarshal(data, aux); err != nil { return err } h.Timestamp = time.UnixMilli(aux.Timestamp) + errorText, err := hookErrorText(aux.Error) + if err != nil { + return err + } + h.Error = errorText return nil } +// hookErrorText decodes the error of an errorOccurred or sessionEnd hook input. +// The CLI sends an error object ({"name", "message", "stack"} or {"message"}): +// its message becomes the error text, and an object without a string message +// becomes its compact JSON. A string passes through, and null or an absent +// error leaves the text empty. +func hookErrorText(raw json.RawMessage) (string, error) { + raw = bytes.TrimSpace(raw) + if len(raw) == 0 || bytes.Equal(raw, []byte("null")) { + return "", nil + } + if raw[0] == '{' { + var object map[string]json.RawMessage + if err := json.Unmarshal(raw, &object); err != nil { + return "", err + } + if message := bytes.TrimSpace(object["message"]); len(message) > 0 && message[0] == '"' { + var text string + if err := json.Unmarshal(message, &text); err != nil { + return "", err + } + return text, nil + } + var compact bytes.Buffer + if err := json.Compact(&compact, raw); err != nil { + return "", err + } + return compact.String(), nil + } + var text string + if err := json.Unmarshal(raw, &text); err != nil { + return "", err + } + return text, nil +} + // ErrorOccurredHookOutput is the output for an error-occurred hook type ErrorOccurredHookOutput struct { SuppressOutput bool `json:"suppressOutput,omitempty"` @@ -990,6 +1064,9 @@ type ErrorOccurredHandler func(input ErrorOccurredHookInput, invocation HookInvo // AgentStopHookInput is the input for an agent-stop hook. type AgentStopHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -1032,6 +1109,9 @@ type AgentStopHandler func(input AgentStopHookInput, invocation HookInvocation) // SubagentStartHookInput is the input before a subagent's first turn. type SubagentStartHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -1074,6 +1154,7 @@ type SubagentStartHandler func(input SubagentStartHookInput, invocation HookInvo // SubagentStopHookInput is the input after a subagent completes a turn. type SubagentStopHookInput struct { + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -1124,6 +1205,9 @@ type SubagentStopHandler func(input SubagentStopHookInput, invocation HookInvoca // PreMCPToolCallHookInput is the input for a pre-mcp-tool-call hook type PreMCPToolCallHookInput struct { + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType,omitempty"` + ParentAgentID string `json:"parentAgentId,omitempty"` SessionID string `json:"sessionId"` Timestamp time.Time `json:"-"` WorkingDirectory string `json:"cwd"` @@ -1252,6 +1336,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 @@ -1477,6 +1563,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 @@ -1515,9 +1608,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 @@ -1527,6 +1620,11 @@ type SessionConfig struct { // and retrieval. When false, session content is not written to or read from the // shared session store. EnableSessionStore *bool + // EnableGitHubCacheStore, when true, requests the read-only GitHub cache store + // (the github-cache-store capability). The runtime registers the github_sql + // tool only when its GITHUB_CACHE_STORE feature flag is also on. Defaults to + // disabled. + EnableGitHubCacheStore *bool // EnableSkills, when non-nil, controls skill loading (including builtin skills // and discovered skill directories). When false, no skills are loaded regardless // of SkillDirectories or EnableConfigDiscovery settings. @@ -1670,7 +1768,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. @@ -1678,13 +1780,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 @@ -1820,6 +1938,11 @@ type SessionConfig struct { // controls for this session. Conflicting changes are rejected; overridable // managed defaults remain mutable. EnforceManagedModelDefaults *bool + // InteractiveUser declares that a human drives this session's conversation, + // enabling user-message analysis telemetry for user-authored messages. + // Automated messages remain excluded. Defaults to false for SDK sessions. + // Ignored for cloud sessions. + InteractiveUser *bool // ManagedSettings supplies host-injected enterprise managed settings for // the session. Unlike EnableManagedSettings (which asks the runtime to // self-fetch account/org and device policy), this provides the managed @@ -2230,9 +2353,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. @@ -2240,6 +2363,11 @@ type ResumeSessionConfig struct { // EnableSessionStore, when non-nil, controls the cross-session store for search // and retrieval across sessions. EnableSessionStore *bool + // EnableGitHubCacheStore, when true, requests the read-only GitHub cache store + // (the github-cache-store capability). The runtime registers the github_sql + // tool only when its GITHUB_CACHE_STORE feature flag is also on. Defaults to + // disabled. + EnableGitHubCacheStore *bool // EnableSkills, when non-nil, controls skill loading. EnableSkills *bool // Streaming enables streaming of assistant message and reasoning chunks. @@ -2272,7 +2400,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 @@ -2281,13 +2413,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 @@ -2402,6 +2550,9 @@ type ResumeSessionConfig struct { // EnforceManagedModelDefaults re-applies managed model lock enforcement on // resume. See SessionConfig.EnforceManagedModelDefaults. EnforceManagedModelDefaults *bool + // InteractiveUser re-supplies that a human drives this session. It is not + // persisted and is ignored if the session is already active in the runtime. + InteractiveUser *bool // ManagedSettings re-injects host-provided managed settings on resume. See // SessionConfig.ManagedSettings. It must be re-supplied on resume: it // replaces the prior injected layer, and omitting it clears that layer so @@ -2853,6 +3004,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"` @@ -2906,11 +3058,14 @@ type createSessionRequest struct { EnableFileHooks *bool `json:"enableFileHooks,omitempty"` EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` EnableSessionStore *bool `json:"enableSessionStore,omitempty"` + EnableGitHubCacheStore *bool `json:"enableGitHubCacheStore,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"` @@ -2936,6 +3091,7 @@ type createSessionRequest struct { FeatureFlags *map[string]bool `json:"featureFlags,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` EnforceManagedModelDefaults *bool `json:"enforceManagedModelDefaults,omitempty"` + InteractiveUser *bool `json:"interactiveUser,omitempty"` ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` @@ -2999,6 +3155,7 @@ type resumeSessionRequest struct { EnableFileHooks *bool `json:"enableFileHooks,omitempty"` EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` EnableSessionStore *bool `json:"enableSessionStore,omitempty"` + EnableGitHubCacheStore *bool `json:"enableGitHubCacheStore,omitempty"` EnableSkills *bool `json:"enableSkills,omitempty"` HasSkillProvider *bool `json:"hasSkillProvider,omitempty"` DisableResume *bool `json:"disableResume,omitempty"` @@ -3015,9 +3172,11 @@ 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"` @@ -3043,6 +3202,7 @@ type resumeSessionRequest struct { FeatureFlags *map[string]bool `json:"featureFlags,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` EnforceManagedModelDefaults *bool `json:"enforceManagedModelDefaults,omitempty"` + InteractiveUser *bool `json:"interactiveUser,omitempty"` ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` diff --git a/go/zsession_events.go b/go/zsession_events.go index 3755ff6e4d..81bb0f2c25 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -955,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..ecb3bff3e5 100644 --- a/java/README.md +++ b/java/README.md @@ -289,6 +289,12 @@ Pass `new ResumeSessionConfig().setContinuePendingWork(true)` to instead. Leave the option unset to use the runtime default. Re-register any external tools needed to handle continued work. +`SessionConfig.setInteractiveUser(true)` declares that a person types the +session's messages, as in an editor chat panel. This enables the runtime's +user-message analysis telemetry for user-authored messages. Automated messages +stay excluded. The default is off, and cloud sessions ignore it. Re-supply it +through `ResumeSessionConfig` on a cold resume. + For rotating per-session GitHub credentials, use `SessionConfig.setGitHubTokenProvider(...)` (or the equivalent `ResumeSessionConfig` setter) instead of `setGitHubToken(...)`: @@ -682,10 +688,25 @@ 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. +`enableGitHubCacheStore` on `SessionConfig` and `ResumeSessionConfig` requests the read-only GitHub cache store (`github-cache-store` capability) on create or resume. The runtime registers the `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is also on. Defaults to disabled in every mode. + ## Memory Sessions can opt into persistent memory, allowing the agent to read and write memory across turns. Memory is configured per session and applies to both `createSession` and `resumeSession`. diff --git a/java/scripts/codegen/java.ts b/java/scripts/codegen/java.ts index 36ebc4a674..9f932aef75 100644 --- a/java/scripts/codegen/java.ts +++ b/java/scripts/codegen/java.ts @@ -2398,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; } @@ -2467,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" ? { @@ -3158,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" && @@ -3172,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" && @@ -3180,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"; } @@ -3187,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])})`; @@ -3209,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/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/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/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/SessionCompactionCompleteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java index 046317ee26..5f82874b0c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java @@ -56,8 +56,6 @@ public record SessionCompactionCompleteEventData( @JsonProperty("responsesReasoning") ResponsesReasoning responsesReasoning, /** Authoritative active-workflow reminder appended to the compacted context */ @JsonProperty("activeWorkflowSummary") String activeWorkflowSummary, - /** Legacy active-workflow reminder retained for replay compatibility */ - @JsonProperty("activeFactorySummary") String activeFactorySummary, /** Canonical model identifier used for model-specific behavior when replaying compaction */ @JsonProperty("behaviorModelId") String behaviorModelId, /** Checkpoint snapshot number created for recovery */ 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/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/SubagentStartedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java index 8c8cde1c90..a2e907111d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java @@ -48,8 +48,6 @@ public record SubagentStartedEventData( @JsonProperty("taskModelSource") SubagentTaskModelSource taskModelSource, /** Authority or runtime mechanism responsible for sub-agent model selection, when known at start. */ @JsonProperty("modelSelectionSource") SubagentModelSelectionSource modelSelectionSource, - /** Legacy root id of the workflow run that spawned this sub-agent. New consumers should use workflowRunId. */ - @JsonProperty("factoryRunId") String factoryRunId, /** Root id of the workflow run that spawned this sub-agent, when it was spawned by one. */ @JsonProperty("workflowRunId") String workflowRunId, /** Task-registry ID of the spawning sub-agent. Absent when the root session spawned this child. */ 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/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/CatalogAgentPluginCandidate.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidate.java index 929a3924b4..b260b27c22 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidate.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidate.java @@ -29,7 +29,7 @@ public final class CatalogAgentPluginCandidate extends CatalogCandidate { @Override public String getKind() { return kind; } - /** Canonical Agent Plugin media type. */ + /** Media type naming the plugin's format. */ @JsonProperty("mediaType") private CatalogAgentPluginMediaType mediaType; @@ -61,6 +61,14 @@ public final class CatalogAgentPluginCandidate extends CatalogCandidate { @JsonProperty("compatibilityTags") private List compatibilityTags; + /** Whether this plugin can be installed in the selected session. Present only when the caller requires the agent-plugin-confirmed-installation capability. */ + @JsonProperty("installability") + private CatalogAgentPluginInstallability installability; + + /** Reviewable install plan built only from verified catalogue install data. Present exactly when installability is `installable`; the runtime never derives it from the repository source, display text, or identity. */ + @JsonProperty("installPlan") + private CatalogAgentPluginInstallPlan installPlan; + /** Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw data, or content digest. */ @JsonProperty("provenance") private CatalogAgentPluginCandidateProvenance provenance; @@ -93,6 +101,12 @@ public final class CatalogAgentPluginCandidate extends CatalogCandidate { public List getCompatibilityTags() { return compatibilityTags; } public void setCompatibilityTags(List compatibilityTags) { this.compatibilityTags = compatibilityTags; } + public CatalogAgentPluginInstallability getInstallability() { return installability; } + public void setInstallability(CatalogAgentPluginInstallability installability) { this.installability = installability; } + + public CatalogAgentPluginInstallPlan getInstallPlan() { return installPlan; } + public void setInstallPlan(CatalogAgentPluginInstallPlan installPlan) { this.installPlan = installPlan; } + public CatalogAgentPluginCandidateProvenance getProvenance() { return provenance; } public void setProvenance(CatalogAgentPluginCandidateProvenance provenance) { this.provenance = provenance; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidateProvenance.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidateProvenance.java index d3e65be949..ff6316dc13 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidateProvenance.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginCandidateProvenance.java @@ -25,7 +25,7 @@ public record CatalogAgentPluginCandidateProvenance( @JsonProperty("authority") String authority, /** ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. */ @JsonProperty("observedAt") String observedAt, - /** Canonical Agent Plugin media type. */ + /** Media type naming the plugin's format. */ @JsonProperty("mediaType") CatalogAgentPluginMediaType mediaType ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginComponent.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginComponent.java new file mode 100644 index 0000000000..e5d44f67f2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginComponent.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 javax.annotation.processing.Generated; + +/** + * A kind of component an Agent Plugin contributes + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum CatalogAgentPluginComponent { + /** The {@code agent} variant. */ + AGENT("agent"), + /** The {@code command} variant. */ + COMMAND("command"), + /** The {@code skill} variant. */ + SKILL("skill"), + /** The {@code hook} variant. */ + HOOK("hook"), + /** The {@code mcp-server} variant. */ + MCP_SERVER("mcp-server"), + /** The {@code lsp-server} variant. */ + LSP_SERVER("lsp-server"), + /** The {@code extension} variant. */ + EXTENSION("extension"), + /** The {@code canvas} variant. */ + CANVAS("canvas"); + + private final String value; + CatalogAgentPluginComponent(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static CatalogAgentPluginComponent fromValue(String value) { + for (CatalogAgentPluginComponent v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown CatalogAgentPluginComponent value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallPlan.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallPlan.java new file mode 100644 index 0000000000..d488838951 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallPlan.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 java.util.List; +import javax.annotation.processing.Generated; + +/** + * What installing an Agent Plugin would do, for the user to review and confirm. Provisional: the catalogue install data it is built from is not final. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CatalogAgentPluginInstallPlan( + /** Every valid install source, in preference order: materialization, then marketplace, then repository. Each kind appears at most once. */ + @JsonProperty("sources") List sources, + /** The plugin's name, as named in its marketplace or manifest. */ + @JsonProperty("pluginName") String pluginName, + /** Exact plugin version to install. A plan with a marketplace source carries at least one of version and commit. */ + @JsonProperty("version") String version, + /** Exact full commit ID to install, in lowercase hex. Always present when the plan has a repository source; a plan with a marketplace source carries at least one of version and commit. */ + @JsonProperty("commit") String commit, + /** Components the plugin contributes, in canonical order. */ + @JsonProperty("components") List components, + /** Canvas IDs the plugin contributes. Non-empty exactly when components include `canvas`. */ + @JsonProperty("canvasIds") List canvasIds, + /** Extension IDs the plugin contributes. Non-empty exactly when components include `extension`. */ + @JsonProperty("extensionIds") List extensionIds, + /** MCP servers the plugin depends on but does not bundle. */ + @JsonProperty("mcpDependencies") List mcpDependencies, + /** Connectors the plugin depends on. */ + @JsonProperty("connectorDependencies") List connectorDependencies +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallSource.java new file mode 100644 index 0000000000..8ba3e644c7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallSource.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.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import javax.annotation.processing.Generated; + +/** + * One place an Agent Plugin can install from: Agent Finder's materialisation, a marketplace, or a GitHub repository. Each variant carries only its own fields. + * + * @since 1.0.0 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "kind", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = CatalogAgentPluginMaterializationSource.class, name = "materialization"), + @JsonSubTypes.Type(value = CatalogAgentPluginMarketplaceSource.class, name = "marketplace"), + @JsonSubTypes.Type(value = CatalogAgentPluginRepositorySource.class, name = "repository") +}) +@JsonIgnoreProperties(ignoreUnknown = true) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public abstract class CatalogAgentPluginInstallSource { + + /** + * Returns the discriminator value for this variant. + * + * @return the kind discriminator + */ + public abstract String getKind(); +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallability.java new file mode 100644 index 0000000000..3da72fd410 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginInstallability.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; + +/** + * Typed installability state for an Agent Plugin candidate + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum CatalogAgentPluginInstallability { + /** The {@code installable} variant. */ + INSTALLABLE("installable"), + /** The {@code install-data-unavailable} variant. */ + INSTALL_DATA_UNAVAILABLE("install-data-unavailable"), + /** The {@code feature-disabled} variant. */ + FEATURE_DISABLED("feature-disabled"), + /** The {@code not-installable-kind} variant. */ + NOT_INSTALLABLE_KIND("not-installable-kind"); + + private final String value; + CatalogAgentPluginInstallability(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static CatalogAgentPluginInstallability fromValue(String value) { + for (CatalogAgentPluginInstallability v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown CatalogAgentPluginInstallability value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMarketplaceSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMarketplaceSource.java new file mode 100644 index 0000000000..df5f25e4b2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMarketplaceSource.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 javax.annotation.processing.Generated; + +/** + * A plugin marketplace named by verified catalogue install data, installed as `pluginName@name`. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class CatalogAgentPluginMarketplaceSource extends CatalogAgentPluginInstallSource { + + @JsonProperty("kind") + private final String kind = "marketplace"; + + @Override + public String getKind() { return kind; } + + /** Marketplace name. */ + @JsonProperty("name") + private String name; + + /** Canonical lowercase GitHub owner/repository hosting the marketplace. */ + @JsonProperty("repository") + private String repository; + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getRepository() { return repository; } + public void setRepository(String repository) { this.repository = repository; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMaterializationSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMaterializationSource.java new file mode 100644 index 0000000000..5ee9b1a83d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMaterializationSource.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 javax.annotation.processing.Generated; + +/** + * Agent Finder's verified materialisation of the plugin. Present only when the candidate carries a complete materialisation reference; the reference itself stays runtime-private. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class CatalogAgentPluginMaterializationSource extends CatalogAgentPluginInstallSource { + + @JsonProperty("kind") + private final String kind = "materialization"; + + @Override + public String getKind() { return kind; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMediaType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMediaType.java index 62a1e5e70f..8e1cb2c536 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMediaType.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginMediaType.java @@ -10,14 +10,18 @@ import javax.annotation.processing.Generated; /** - * Canonical Agent Plugin media type + * Media type naming the plugin format of an Agent Plugin candidate. Values other than `application/vnd.github.copilot-plugin` appear only when the caller requires the agent-plugin-formats capability. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") public enum CatalogAgentPluginMediaType { /** The {@code application/vnd.github.copilot-plugin} variant. */ - APPLICATION_VND_GITHUB_COPILOT_PLUGIN("application/vnd.github.copilot-plugin"); + APPLICATION_VND_GITHUB_COPILOT_PLUGIN("application/vnd.github.copilot-plugin"), + /** The {@code application/vnd.anthropic.claude-plugin+json} variant. */ + APPLICATION_VND_ANTHROPIC_CLAUDE_PLUGIN_JSON("application/vnd.anthropic.claude-plugin+json"), + /** The {@code application/agent-plugin+json} variant. */ + APPLICATION_AGENT_PLUGIN_JSON("application/agent-plugin+json"); private final String value; CatalogAgentPluginMediaType(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginRepositorySource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginRepositorySource.java new file mode 100644 index 0000000000..f6ecd5ba4a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogAgentPluginRepositorySource.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 javax.annotation.processing.Generated; + +/** + * A GitHub repository holding the plugin, installed directly as `owner/repo[:path]` at the plan's commit, without a marketplace. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class CatalogAgentPluginRepositorySource extends CatalogAgentPluginInstallSource { + + @JsonProperty("kind") + private final String kind = "repository"; + + @Override + public String getKind() { return kind; } + + /** Canonical lowercase GitHub owner/repository holding the plugin. */ + @JsonProperty("repository") + private String repository; + + /** Relative POSIX path of the plugin directory in the repository. Omitted when the plugin is at the repository root. */ + @JsonProperty("path") + private String path; + + public String getRepository() { return repository; } + public void setRepository(String repository) { this.repository = repository; } + + public String getPath() { return path; } + public void setPath(String path) { this.path = path; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogCapability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogCapability.java index e782f16aae..7fae5f5f06 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogCapability.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogCapability.java @@ -24,6 +24,8 @@ public enum CatalogCapability { AI_SKILL_DISCOVERY("ai-skill-discovery"), /** The {@code agent-plugin-discovery} variant. */ AGENT_PLUGIN_DISCOVERY("agent-plugin-discovery"), + /** The {@code agent-plugin-formats} variant. */ + AGENT_PLUGIN_FORMATS("agent-plugin-formats"), /** The {@code mcp-install-planning} variant. */ MCP_INSTALL_PLANNING("mcp-install-planning"), /** The {@code multiple-transport-choice} variant. */ @@ -43,7 +45,11 @@ public enum CatalogCapability { /** The {@code mcp-configured-remote-installation} variant. */ MCP_CONFIGURED_REMOTE_INSTALLATION("mcp-configured-remote-installation"), /** The {@code skill-confirmed-installation} variant. */ - SKILL_CONFIRMED_INSTALLATION("skill-confirmed-installation"); + SKILL_CONFIRMED_INSTALLATION("skill-confirmed-installation"), + /** The {@code agent-plugin-confirmed-installation} variant. */ + AGENT_PLUGIN_CONFIRMED_INSTALLATION("agent-plugin-confirmed-installation"), + /** The {@code skill-reference-installation} variant. */ + SKILL_REFERENCE_INSTALLATION("skill-reference-installation"); private final String value; CatalogCapability(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMaterializationReference.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMaterializationReference.java new file mode 100644 index 0000000000..08803d045a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMaterializationReference.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 javax.annotation.processing.Generated; + +/** + * An Agent Finder materialisation reference pinning one exact resource revision. + +It carries no origin or URL: the runtime always builds the descriptor URL on its fixed Agent +Finder authority. It carries no display data either; every reviewed field comes from the +verified descriptor and Skill files. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CatalogMaterializationReference( + /** Agent Finder resource identifier, a positive decimal integer. */ + @JsonProperty("resourceId") String resourceId, + /** Agent Finder materialisation revision identifier, a positive decimal integer. This is not a Git commit. */ + @JsonProperty("revisionId") String revisionId, + /** Materialisation descriptor schema version, currently `1.0.0`. */ + @JsonProperty("schemaVersion") String schemaVersion, + /** Expected `sha256:` digest of the canonical descriptor. */ + @JsonProperty("descriptorDigest") String descriptorDigest, + /** Expected `sha256:` digest of the descriptor's bundle manifest. */ + @JsonProperty("bundleDigest") String bundleDigest +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMediaType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMediaType.java index ddb2841fca..bc42b926e2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMediaType.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CatalogMediaType.java @@ -23,7 +23,11 @@ public enum CatalogMediaType { /** The {@code application/ai-skill} variant. */ APPLICATION_AI_SKILL("application/ai-skill"), /** The {@code application/vnd.github.copilot-plugin} variant. */ - APPLICATION_VND_GITHUB_COPILOT_PLUGIN("application/vnd.github.copilot-plugin"); + APPLICATION_VND_GITHUB_COPILOT_PLUGIN("application/vnd.github.copilot-plugin"), + /** The {@code application/vnd.anthropic.claude-plugin+json} variant. */ + APPLICATION_VND_ANTHROPIC_CLAUDE_PLUGIN_JSON("application/vnd.anthropic.claude-plugin+json"), + /** The {@code application/agent-plugin+json} variant. */ + APPLICATION_AGENT_PLUGIN_JSON("application/agent-plugin+json"); private final String value; CatalogMediaType(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionValidatePoliciesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionValidatePoliciesParams.java new file mode 100644 index 0000000000..a1c8d712e5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionValidatePoliciesParams.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; + +/** + * Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser. + * + * @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 ContentExclusionValidatePoliciesParams( + /** Unparsed JSON text expected to hold an array of content-exclusion policies. */ + @JsonProperty("policiesJson") String policiesJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionValidatePoliciesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionValidatePoliciesResult.java new file mode 100644 index 0000000000..b45bf2329a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionValidatePoliciesResult.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 accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this result. + * + * @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 ContentExclusionValidatePoliciesResult( + /** Validated policy objects in input order. Raw JSON preserves every field returned by the runtime's policy parser. */ + @JsonProperty("policies") List policies +) { +} 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/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/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/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/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/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/McpRegistrySearchParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpRegistrySearchParams.java index e222f69adc..11a06de374 100644 --- 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 @@ -27,6 +27,7 @@ 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, 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/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/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/ServerContentExclusionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerContentExclusionApi.java new file mode 100644 index 0000000000..4df2fb93e5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerContentExclusionApi.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 contentExclusion} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerContentExclusionApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerContentExclusionApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Validates candidate content-exclusion policies and returns them normalized, without starting a session. This runs the same parser the runtime's own policy enforcement uses, so a host cannot accept a policy set the runtime would later reject, nor reject one it would have honored. Call it when policies arrive from outside the runtime — a command-line flag, a configuration file, a host's own settings — and must be checked before a session exists to apply them to. A malformed document fails with a message prefixed `Invalid JSON: `; a well-formed document that violates the policy schema fails with the offending field paths. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture validatePolicies(ContentExclusionValidatePoliciesParams params) { + return caller.invoke("contentExclusion.validatePolicies", params, ContentExclusionValidatePoliciesResult.class); + } + +} 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/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 579ef69f90..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 @@ -30,6 +30,116 @@ public final class ServerManagedSettingsApi { 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); + } + /** * 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. * 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 05e517d864..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 @@ -48,6 +48,17 @@ 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. * 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..07f368980b 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,8 @@ 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 contentExclusion} namespace. */ + public final ServerContentExclusionApi contentExclusion; /** API methods for the {@code tools} namespace. */ public final ServerToolsApi tools; /** API methods for the {@code account} namespace. */ @@ -45,6 +59,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 +69,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 +99,33 @@ 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.contentExclusion = new ServerContentExclusionApi(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..cd229d5943 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; @@ -44,6 +45,17 @@ public CompletableFuture planInstall(SkillsPl return caller.invoke("skills.planInstall", params, SkillInstallationManagementResult.class); } + /** + * Plans installation of the Skill pinned by an Agent Finder materialisation reference without writing files. The runtime retrieves the descriptor only from its fixed Agent Finder authority and applies the same verification as skills.planInstall; the review shows only verified data. Apply the returned plan with skills.applyInstall. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture planInstallFromReference(SkillsPlanInstallFromReferenceParams params) { + return caller.invoke("skills.planInstallFromReference", params, SkillInstallationManagementResult.class); + } + /** * Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled. * @@ -99,4 +111,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/SessionCapability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java index 3611b0680a..baff32f2e4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java @@ -34,6 +34,8 @@ public enum SessionCapability { ELICITATION("elicitation"), /** The {@code session-store} variant. */ SESSION_STORE("session-store"), + /** The {@code github-cache-store} variant. */ + GITHUB_CACHE_STORE("github-cache-store"), /** The {@code mcp-apps} variant. */ MCP_APPS("mcp-apps"), /** The {@code canvas-renderer} variant. */ 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/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..3375bd2a2b 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 @@ -53,6 +53,8 @@ public record SessionOpenOptions( @JsonProperty("enforceManagedModelDefaults") Boolean enforceManagedModelDefaults, /** Permissions-only enterprise policy injected by the SDK host at session create or resume. Composes restrictively with self-fetched and device policy and is not persisted. */ @JsonProperty("managedSettings") SessionManagedSettings managedSettings, + /** Declares that a human drives this session's conversation, enabling user-message analysis (sentiment, task completion criteria, and task analytics) for user-authored messages. Automated messages remain excluded. Defaults to false for SDK sessions. Applied when the session is created or loaded from storage; ignored when resuming a session that is already active, and for cloud sessions. */ + @JsonProperty("interactiveUser") Boolean interactiveUser, /** Opt in to capturing file changes for session rewind and session diff. Capture cannot reconstruct changes made before it was enabled. On create it starts capture from the first turn. It is also honored on resume: for a session that already has tracked prior turns, tracking continues automatically even if this is omitted; passing it on resume additionally enables tracking for an eligible session that has no prior root turn yet. Resuming a session whose prior root turns were never tracked has no restorable baseline, so tracking stays disabled for it and rewind reports file change tracking as unavailable; the resume itself still succeeds, so sessions that predate tracking remain loadable. The opt-in is only rejected when the session can never track (a subagent session, or one without local session storage). It is intentionally absent from the mutable options update because enabling it after edits have occurred would create an incomplete, misleading baseline. Subagents share the parent session's capture store and are not tracked as separate rewind points: a file a subagent writes is attributed to whichever root user turn was open when the capture was staged, just before the tool body ran. A turn cannot open while a staged capture is still in flight, so a subagent tool that staged under the spawning turn stays attributed to it however late the write lands, while a capture it stages after the user's next message belongs to that later turn. Attribution decides which turn's rewind point counts and file preview include that write; it does not narrow which rewinds revert it, because a rewind restores every capture from the selected turn onward, so the earlier spawning turn reverts it as well. */ @JsonProperty("enableFileChangeTracking") Boolean enableFileChangeTracking, /** Feature-flag values resolved by the host. */ @@ -117,11 +119,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/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/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/SkillInstallationFailureReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillInstallationFailureReason.java index 7751666ecd..eaece40dbc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillInstallationFailureReason.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillInstallationFailureReason.java @@ -75,7 +75,17 @@ public enum SkillInstallationFailureReason { /** The {@code configuration-modified} variant. */ CONFIGURATION_MODIFIED("configuration-modified"), /** The {@code write-failed} variant. */ - WRITE_FAILED("write-failed"); + WRITE_FAILED("write-failed"), + /** The {@code invalid-reference} variant. */ + INVALID_REFERENCE("invalid-reference"), + /** The {@code descriptor-not-found} variant. */ + DESCRIPTOR_NOT_FOUND("descriptor-not-found"), + /** The {@code descriptor-withdrawn} variant. */ + DESCRIPTOR_WITHDRAWN("descriptor-withdrawn"), + /** The {@code digest-mismatch} variant. */ + DIGEST_MISMATCH("digest-mismatch"), + /** The {@code unsupported-resource} variant. */ + UNSUPPORTED_RESOURCE("unsupported-resource"); private final String value; SkillInstallationFailureReason(String value) { this.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/SkillsPlanInstallFromReferenceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsPlanInstallFromReferenceParams.java new file mode 100644 index 0000000000..432d505989 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsPlanInstallFromReferenceParams.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; + +/** + * Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate. + * + * @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 SkillsPlanInstallFromReferenceParams( + /** Required authenticated bound catalogue, skill-confirmed-installation and skill-reference-installation capabilities. */ + @JsonProperty("contract") CatalogClientContract contract, + /** Materialisation reference to verify and plan. */ + @JsonProperty("reference") CatalogMaterializationReference reference, + /** Existing local session attached to this connection. */ + @JsonProperty("policySessionId") String policySessionId +) { +} 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 b277c09fa8..de6081b39d 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java @@ -1442,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 71cf2fcded..156f496b57 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,6 +197,8 @@ 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()); @@ -215,6 +218,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess config.getEnableFileHooks().ifPresent(request::setEnableFileHooks); config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); + config.getEnableGitHubCacheStore().ifPresent(request::setEnableGitHubCacheStore); config.getEnableSkills().ifPresent(request::setEnableSkills); if (config.getSkillProvider() != null) { request.setHasSkillProvider(true); @@ -250,6 +254,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); config.getEnforceManagedModelDefaults().ifPresent(request::setEnforceManagedModelDefaults); + config.getInteractiveUser().ifPresent(request::setInteractiveUser); request.setManagedSettings(config.getManagedSettings()); return request; @@ -339,6 +344,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo config.getEnableFileHooks().ifPresent(request::setEnableFileHooks); config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); + config.getEnableGitHubCacheStore().ifPresent(request::setEnableGitHubCacheStore); config.getEnableSkills().ifPresent(request::setEnableSkills); if (config.getSkillProvider() != null) { request.setHasSkillProvider(true); @@ -363,6 +369,8 @@ 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()); @@ -399,6 +407,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); config.getEnforceManagedModelDefaults().ifPresent(request::setEnforceManagedModelDefaults); + config.getInteractiveUser().ifPresent(request::setInteractiveUser); request.setManagedSettings(config.getManagedSettings()); return request; 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/AgentStopHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java index fceea8b72c..78a693f34e 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java @@ -15,6 +15,42 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class AgentStopHookInput { + @JsonProperty("agentId") + private String agentId; + + public String getAgentId() { + return agentId; + } + + public AgentStopHookInput setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + @JsonProperty("agentType") + private String agentType; + + public String getAgentType() { + return agentType; + } + + public AgentStopHookInput setAgentType(String agentType) { + this.agentType = agentType; + return this; + } + + @JsonProperty("parentAgentId") + private String parentAgentId; + + public String getParentAgentId() { + return parentAgentId; + } + + public AgentStopHookInput setParentAgentId(String parentAgentId) { + this.parentAgentId = parentAgentId; + return this; + } + @JsonProperty("sessionId") private String sessionId; 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 fa5e7d3e16..dab6bd415c 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; @@ -204,6 +213,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSessionStore; + @JsonProperty("enableGitHubCacheStore") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableGitHubCacheStore; + @JsonProperty("enableSkills") @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSkills; @@ -267,6 +280,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enforceManagedModelDefaults; + @JsonProperty("interactiveUser") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean interactiveUser; + @JsonProperty("managedSettings") @JsonInclude(JsonInclude.Include.NON_NULL) private ManagedSettings managedSettings; @@ -291,6 +308,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; @@ -723,6 +750,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); @@ -965,6 +1017,25 @@ public void clearEnableSessionStore() { this.enableSessionStore = null; } + /** Gets enable GitHub cache store flag. @return the flag */ + public Boolean getEnableGitHubCacheStore() { + return enableGitHubCacheStore; + } + + /** + * Sets enable GitHub cache store flag. @param enableGitHubCacheStore the flag + */ + public void setEnableGitHubCacheStore(boolean enableGitHubCacheStore) { + this.enableGitHubCacheStore = enableGitHubCacheStore; + } + + /** + * Clears the enableGitHubCacheStore setting, reverting to the default behavior. + */ + public void clearEnableGitHubCacheStore() { + this.enableGitHubCacheStore = null; + } + /** Gets enable skills flag. @return the flag */ public Boolean getEnableSkills() { return enableSkills; @@ -1276,6 +1347,24 @@ public void clearEnforceManagedModelDefaults() { this.enforceManagedModelDefaults = null; } + /** @return whether this session is human-driven, or {@code null} when unset */ + public Boolean getInteractiveUser() { + return interactiveUser; + } + + /** + * @param interactiveUser + * whether a human drives this session + */ + public void setInteractiveUser(boolean interactiveUser) { + this.interactiveUser = interactiveUser; + } + + /** Clears the interactiveUser setting, reverting to the default behavior. */ + public void clearInteractiveUser() { + this.interactiveUser = null; + } + /** @return host-injected managed settings, or {@code null} when unset */ public ManagedSettings getManagedSettings() { return managedSettings; 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/PostToolUseFailureHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java index 820976b96f..c7e627a590 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java @@ -20,6 +20,42 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class PostToolUseFailureHookInput { + @JsonProperty("agentId") + private String agentId; + + public String getAgentId() { + return agentId; + } + + public PostToolUseFailureHookInput setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + @JsonProperty("agentType") + private String agentType; + + public String getAgentType() { + return agentType; + } + + public PostToolUseFailureHookInput setAgentType(String agentType) { + this.agentType = agentType; + return this; + } + + @JsonProperty("parentAgentId") + private String parentAgentId; + + public String getParentAgentId() { + return parentAgentId; + } + + public PostToolUseFailureHookInput setParentAgentId(String parentAgentId) { + this.parentAgentId = parentAgentId; + return this; + } + @JsonProperty("sessionId") private String sessionId; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java index 9da5aee883..90bc779b82 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java @@ -16,6 +16,42 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class PostToolUseHookInput { + @JsonProperty("agentId") + private String agentId; + + public String getAgentId() { + return agentId; + } + + public PostToolUseHookInput setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + @JsonProperty("agentType") + private String agentType; + + public String getAgentType() { + return agentType; + } + + public PostToolUseHookInput setAgentType(String agentType) { + this.agentType = agentType; + return this; + } + + @JsonProperty("parentAgentId") + private String parentAgentId; + + public String getParentAgentId() { + return parentAgentId; + } + + public PostToolUseHookInput setParentAgentId(String parentAgentId) { + this.parentAgentId = parentAgentId; + return this; + } + @JsonProperty("sessionId") private String sessionId; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java index 881065aa86..219f7ba50b 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java @@ -21,6 +21,42 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class PreMcpToolCallHookInput { + @JsonProperty("agentId") + private String agentId; + + public String getAgentId() { + return agentId; + } + + public PreMcpToolCallHookInput setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + @JsonProperty("agentType") + private String agentType; + + public String getAgentType() { + return agentType; + } + + public PreMcpToolCallHookInput setAgentType(String agentType) { + this.agentType = agentType; + return this; + } + + @JsonProperty("parentAgentId") + private String parentAgentId; + + public String getParentAgentId() { + return parentAgentId; + } + + public PreMcpToolCallHookInput setParentAgentId(String parentAgentId) { + this.parentAgentId = parentAgentId; + return this; + } + @JsonProperty("sessionId") private String sessionId; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java index b6cb118590..c3840fc922 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java @@ -16,6 +16,42 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class PreToolUseHookInput { + @JsonProperty("agentId") + private String agentId; + + public String getAgentId() { + return agentId; + } + + public PreToolUseHookInput setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + @JsonProperty("agentType") + private String agentType; + + public String getAgentType() { + return agentType; + } + + public PreToolUseHookInput setAgentType(String agentType) { + this.agentType = agentType; + return this; + } + + @JsonProperty("parentAgentId") + private String parentAgentId; + + public String getParentAgentId() { + return parentAgentId; + } + + public PreToolUseHookInput setParentAgentId(String parentAgentId) { + this.parentAgentId = parentAgentId; + return this; + } + @JsonProperty("sessionId") private String sessionId; 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 44c965bab0..61f0e5a4b8 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 @@ -83,6 +83,7 @@ public class ResumeSessionConfig { private Boolean enableFileHooks; private Boolean enableHostGitOperations; private Boolean enableSessionStore; + private Boolean enableGitHubCacheStore; private Boolean enableSkills; private SkillProvider skillProvider; private String embeddingCacheStorage; @@ -97,6 +98,8 @@ 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; @@ -121,6 +124,7 @@ public class ResumeSessionConfig { private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; private Boolean enforceManagedModelDefaults; + private Boolean interactiveUser; private Map featureFlags; private ManagedSettings managedSettings; @@ -1177,7 +1181,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 @@ -1281,6 +1287,45 @@ public ResumeSessionConfig clearEnableSessionStore() { return this; } + /** + * Gets whether the read-only GitHub cache store is requested. + * + * @return an {@link java.util.Optional} containing {@code true} to request the + * {@code github-cache-store} capability or {@code false} to decline it, + * or {@link java.util.Optional#empty()} to use the default behavior + * (disabled) + */ + @JsonIgnore + public Optional getEnableGitHubCacheStore() { + return Optional.ofNullable(enableGitHubCacheStore); + } + + /** + * Sets whether the read-only GitHub cache store is requested. + *

+ * The runtime registers the {@code github_sql} tool only when its + * {@code GITHUB_CACHE_STORE} feature flag is also on. + * + * @param enableGitHubCacheStore + * {@code true} to request the {@code github-cache-store} capability, + * {@code false} to decline it + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableGitHubCacheStore(boolean enableGitHubCacheStore) { + this.enableGitHubCacheStore = enableGitHubCacheStore; + return this; + } + + /** + * Clears the enableGitHubCacheStore setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableGitHubCacheStore() { + this.enableGitHubCacheStore = null; + return this; + } + /** * Gets whether skill loading is enabled. * @@ -1660,7 +1705,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 */ @@ -1680,6 +1729,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. * @@ -1703,7 +1805,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 */ @@ -2249,6 +2356,27 @@ public ResumeSessionConfig setEnforceManagedModelDefaults(boolean enforceManaged return this; } + /** @return optional declaration that this session is human-driven */ + @JsonIgnore + public Optional getInteractiveUser() { + return Optional.ofNullable(interactiveUser); + } + + /** + * Re-supplies that a human drives this session so a session loaded from storage + * keeps the declaration. It is not persisted and is ignored if the session is + * already active in the runtime process. + * + * @param interactiveUser + * {@code true} for interactive hosts such as editors or IDE chat + * panels + * @return this config for method chaining + */ + public ResumeSessionConfig setInteractiveUser(boolean interactiveUser) { + this.interactiveUser = interactiveUser; + return this; + } + /** @return host-injected managed settings, or {@code null} when unset */ public ManagedSettings getManagedSettings() { return managedSettings; @@ -2321,6 +2449,7 @@ public ResumeSessionConfig clone() { copy.enableFileHooks = this.enableFileHooks; copy.enableHostGitOperations = this.enableHostGitOperations; copy.enableSessionStore = this.enableSessionStore; + copy.enableGitHubCacheStore = this.enableGitHubCacheStore; copy.enableSkills = this.enableSkills; copy.skillProvider = this.skillProvider; copy.embeddingCacheStorage = this.embeddingCacheStorage; @@ -2335,6 +2464,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; @@ -2364,6 +2495,7 @@ public ResumeSessionConfig clone() { copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; copy.enforceManagedModelDefaults = this.enforceManagedModelDefaults; + copy.interactiveUser = this.interactiveUser; copy.managedSettings = this.managedSettings; return copy; } 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 4a5e2a9a7c..ae91dc71a1 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 @@ -143,6 +143,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSessionStore; + @JsonProperty("enableGitHubCacheStore") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enableGitHubCacheStore; + @JsonProperty("enableSkills") @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSkills; @@ -197,6 +201,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; @@ -272,6 +282,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enforceManagedModelDefaults; + @JsonProperty("interactiveUser") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean interactiveUser; + @JsonProperty("managedSettings") @JsonInclude(JsonInclude.Include.NON_NULL) private ManagedSettings managedSettings; @@ -760,6 +774,25 @@ public void clearEnableSessionStore() { this.enableSessionStore = null; } + /** Gets enable GitHub cache store flag. @return the flag */ + public Boolean getEnableGitHubCacheStore() { + return enableGitHubCacheStore; + } + + /** + * Sets enable GitHub cache store flag. @param enableGitHubCacheStore the flag + */ + public void setEnableGitHubCacheStore(boolean enableGitHubCacheStore) { + this.enableGitHubCacheStore = enableGitHubCacheStore; + } + + /** + * Clears the enableGitHubCacheStore setting, reverting to the default behavior. + */ + public void clearEnableGitHubCacheStore() { + this.enableGitHubCacheStore = null; + } + /** Gets enable skills flag. @return the flag */ public Boolean getEnableSkills() { return enableSkills; @@ -996,6 +1029,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); @@ -1306,6 +1364,24 @@ public void clearEnforceManagedModelDefaults() { this.enforceManagedModelDefaults = null; } + /** @return whether this session is human-driven, or {@code null} when unset */ + public Boolean getInteractiveUser() { + return interactiveUser; + } + + /** + * @param interactiveUser + * whether a human drives this session + */ + public void setInteractiveUser(boolean interactiveUser) { + this.interactiveUser = interactiveUser; + } + + /** Clears the interactiveUser setting, reverting to the default behavior. */ + public void clearInteractiveUser() { + this.interactiveUser = null; + } + /** @return host-injected managed settings, or {@code null} when unset */ public ManagedSettings getManagedSettings() { return managedSettings; 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 e912527abc..8ae3f46b75 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,6 +85,8 @@ 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; @@ -102,6 +105,7 @@ public class SessionConfig { private Boolean enableFileHooks; private Boolean enableHostGitOperations; private Boolean enableSessionStore; + private Boolean enableGitHubCacheStore; private Boolean enableSkills; private SkillProvider skillProvider; private String embeddingCacheStorage; @@ -121,6 +125,7 @@ public class SessionConfig { private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; private Boolean enforceManagedModelDefaults; + private Boolean interactiveUser; private Map featureFlags; private ManagedSettings managedSettings; @@ -147,6 +152,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. * @@ -1236,7 +1266,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 */ @@ -1260,6 +1294,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. * @@ -1283,7 +1370,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 */ @@ -1648,7 +1740,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 @@ -1752,6 +1846,45 @@ public SessionConfig clearEnableSessionStore() { return this; } + /** + * Gets whether the read-only GitHub cache store is requested. + * + * @return an {@link java.util.Optional} containing {@code true} to request the + * {@code github-cache-store} capability or {@code false} to decline it, + * or {@link java.util.Optional#empty()} to use the default behavior + * (disabled) + */ + @JsonIgnore + public Optional getEnableGitHubCacheStore() { + return Optional.ofNullable(enableGitHubCacheStore); + } + + /** + * Sets whether the read-only GitHub cache store is requested. + *

+ * The runtime registers the {@code github_sql} tool only when its + * {@code GITHUB_CACHE_STORE} feature flag is also on. + * + * @param enableGitHubCacheStore + * {@code true} to request the {@code github-cache-store} capability, + * {@code false} to decline it + * @return this config instance for method chaining + */ + public SessionConfig setEnableGitHubCacheStore(boolean enableGitHubCacheStore) { + this.enableGitHubCacheStore = enableGitHubCacheStore; + return this; + } + + /** + * Clears the enableGitHubCacheStore setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableGitHubCacheStore() { + this.enableGitHubCacheStore = null; + return this; + } + /** * Gets whether skill loading is enabled. * @@ -2341,6 +2474,32 @@ public SessionConfig setEnforceManagedModelDefaults(boolean enforceManagedModelD return this; } + /** + * Gets whether this session is human-driven. + * + * @return an optional containing the declaration, or empty when unset + */ + @JsonIgnore + public Optional getInteractiveUser() { + return Optional.ofNullable(interactiveUser); + } + + /** + * Declares that a human drives this session's conversation, enabling + * user-message analysis telemetry for user-authored messages. Automated + * messages remain excluded. Defaults to false for SDK sessions. Ignored for + * cloud sessions. + * + * @param interactiveUser + * {@code true} for interactive hosts such as editors or IDE chat + * panels + * @return this config instance for method chaining + */ + public SessionConfig setInteractiveUser(boolean interactiveUser) { + this.interactiveUser = interactiveUser; + return this; + } + /** * Gets host-injected managed settings for this session. * @@ -2423,6 +2582,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; @@ -2445,6 +2606,7 @@ public SessionConfig clone() { copy.enableFileHooks = this.enableFileHooks; copy.enableHostGitOperations = this.enableHostGitOperations; copy.enableSessionStore = this.enableSessionStore; + copy.enableGitHubCacheStore = this.enableGitHubCacheStore; copy.enableSkills = this.enableSkills; copy.skillProvider = this.skillProvider; copy.embeddingCacheStorage = this.embeddingCacheStorage; @@ -2465,6 +2627,7 @@ public SessionConfig clone() { copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; copy.enforceManagedModelDefaults = this.enforceManagedModelDefaults; + copy.interactiveUser = this.interactiveUser; copy.managedSettings = this.managedSettings; return copy; } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java index f29b698385..0a96bb520c 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java @@ -4,8 +4,17 @@ package com.github.copilot.rpc; +import java.io.IOException; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.deser.std.StringDeserializer; /** * Input for a session-end hook. @@ -25,12 +34,45 @@ * @param finalMessage * the final message, or {@code null} * @param error - * the error message, or {@code null} + * the error message, or {@code null}; the CLI sends an error object, + * whose {@code message} this is * @since 1.0.7 + * @param agentId + * optional subagent identity, absent for root hooks and older + * runtimes + * @param agentType + * optional subagent identity, absent for root hooks and older + * runtimes + * @param parentAgentId + * optional subagent identity, absent for root hooks and older + * runtimes */ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionEndHookInput(@JsonProperty("sessionId") String sessionId, @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, @JsonProperty("reason") String reason, @JsonProperty("finalMessage") String finalMessage, - @JsonProperty("error") String error) { + @JsonProperty("error") @JsonDeserialize(using = HookErrorTextDeserializer.class) String error, + @JsonProperty("agentId") String agentId, @JsonProperty("agentType") String agentType, + @JsonProperty("parentAgentId") String parentAgentId) { + public SessionEndHookInput(String sessionId, long timestamp, String cwd, String reason, String finalMessage, + String error) { + this(sessionId, timestamp, cwd, reason, finalMessage, error, null, null, null); + } + + /** + * Reads the CLI's error object ({@code {"message"}}) as its message, and an + * object without a string message as its JSON. A string passes through. + */ + private static final class HookErrorTextDeserializer extends JsonDeserializer { + + @Override + public String deserialize(JsonParser parser, DeserializationContext context) throws IOException { + if (parser.currentToken() == JsonToken.START_OBJECT) { + JsonNode error = context.readTree(parser); + JsonNode message = error.get("message"); + return message != null && message.isTextual() ? message.textValue() : error.toString(); + } + return StringDeserializer.instance.deserialize(parser, context); + } + } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java index d6e5b37596..1a044d289f 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java @@ -24,9 +24,23 @@ * @param initialPrompt * the initial prompt, or {@code null} * @since 1.0.7 + * @param agentId + * optional subagent identity, absent for root hooks and older + * runtimes + * @param agentType + * optional subagent identity, absent for root hooks and older + * runtimes + * @param parentAgentId + * optional subagent identity, absent for root hooks and older + * runtimes */ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionStartHookInput(@JsonProperty("sessionId") String sessionId, @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, - @JsonProperty("source") String source, @JsonProperty("initialPrompt") String initialPrompt) { + @JsonProperty("source") String source, @JsonProperty("initialPrompt") String initialPrompt, + @JsonProperty("agentId") String agentId, @JsonProperty("agentType") String agentType, + @JsonProperty("parentAgentId") String parentAgentId) { + public SessionStartHookInput(String sessionId, long timestamp, String cwd, String source, String initialPrompt) { + this(sessionId, timestamp, cwd, source, initialPrompt, null, null, null); + } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookInput.java index 0ab4d426b4..5e67057469 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookInput.java @@ -24,11 +24,26 @@ * the optional subagent display name * @param agentDescription * the optional subagent description + * @param agentId + * optional subagent identity, absent for root hooks and older + * runtimes + * @param agentType + * optional subagent identity, absent for root hooks and older + * runtimes + * @param parentAgentId + * optional subagent identity, absent for root hooks and older + * runtimes */ @JsonIgnoreProperties(ignoreUnknown = true) public record SubagentStartHookInput(@JsonProperty("sessionId") String sessionId, @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, @JsonProperty("transcriptPath") String transcriptPath, @JsonProperty("agentName") String agentName, @JsonProperty("agentDisplayName") String agentDisplayName, - @JsonProperty("agentDescription") String agentDescription) { + @JsonProperty("agentDescription") String agentDescription, @JsonProperty("agentId") String agentId, + @JsonProperty("agentType") String agentType, @JsonProperty("parentAgentId") String parentAgentId) { + public SubagentStartHookInput(String sessionId, long timestamp, String cwd, String transcriptPath, String agentName, + String agentDisplayName, String agentDescription) { + this(sessionId, timestamp, cwd, transcriptPath, agentName, agentDisplayName, agentDescription, null, null, + null); + } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookInput.java index 299238e034..6fea33e288 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookInput.java @@ -32,6 +32,9 @@ * the reason the subagent stopped * @param response * the subagent's last assistant response + * @param parentAgentId + * optional subagent identity, absent for root hooks and older + * runtimes */ @JsonIgnoreProperties(ignoreUnknown = true) public record SubagentStopHookInput(@JsonProperty("sessionId") String sessionId, @@ -40,5 +43,11 @@ public record SubagentStopHookInput(@JsonProperty("sessionId") String sessionId, @JsonProperty("agentType") String agentType, @JsonProperty("agentId") String agentId, @JsonProperty("agentDisplayName") String agentDisplayName, @JsonProperty("agentDescription") String agentDescription, @JsonProperty("stopReason") String stopReason, - @JsonProperty("response") String response) { + @JsonProperty("response") String response, @JsonProperty("parentAgentId") String parentAgentId) { + public SubagentStopHookInput(String sessionId, long timestamp, String cwd, String transcriptPath, String agentName, + String agentType, String agentId, String agentDisplayName, String agentDescription, String stopReason, + String response) { + this(sessionId, timestamp, cwd, transcriptPath, agentName, agentType, agentId, agentDisplayName, + agentDescription, stopReason, response, null); + } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java index 8b37df6773..7ed11ecd02 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java @@ -23,9 +23,22 @@ * @param prompt * the user's prompt text * @since 1.0.7 + * @param agentId + * optional subagent identity, absent for root hooks and older + * runtimes + * @param agentType + * optional subagent identity, absent for root hooks and older + * runtimes + * @param parentAgentId + * optional subagent identity, absent for root hooks and older + * runtimes */ @JsonIgnoreProperties(ignoreUnknown = true) public record UserPromptSubmittedHookInput(@JsonProperty("sessionId") String sessionId, @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, - @JsonProperty("prompt") String prompt) { + @JsonProperty("prompt") String prompt, @JsonProperty("agentId") String agentId, + @JsonProperty("agentType") String agentType, @JsonProperty("parentAgentId") String parentAgentId) { + public UserPromptSubmittedHookInput(String sessionId, long timestamp, String cwd, String prompt) { + this(sessionId, timestamp, cwd, prompt, null, null, null); + } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java index ea17596585..abb064c00d 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java @@ -21,9 +21,24 @@ * @param transformedPrompt * the model-facing prompt after runtime transformations * @since 1.0.11 + * @param agentId + * optional subagent identity, absent for root hooks and older + * runtimes + * @param agentType + * optional subagent identity, absent for root hooks and older + * runtimes + * @param parentAgentId + * optional subagent identity, absent for root hooks and older + * runtimes */ @JsonIgnoreProperties(ignoreUnknown = true) public record UserPromptTransformedHookInput(@JsonProperty("sessionId") String sessionId, @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, - @JsonProperty("prompt") String prompt, @JsonProperty("transformedPrompt") String transformedPrompt) { + @JsonProperty("prompt") String prompt, @JsonProperty("transformedPrompt") String transformedPrompt, + @JsonProperty("agentId") String agentId, @JsonProperty("agentType") String agentType, + @JsonProperty("parentAgentId") String parentAgentId) { + public UserPromptTransformedHookInput(String sessionId, long timestamp, String cwd, String prompt, + String transformedPrompt) { + this(sessionId, timestamp, cwd, prompt, transformedPrompt, null, null, 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/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/ManagedSettingsTest.java b/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java index 7b1910753f..16caf45a1f 100644 --- a/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java @@ -30,24 +30,38 @@ void forwardsManagedSettingsOnCreateAndResume() throws Exception { var managedSettings = new ManagedSettings().setPermissions(permissions); var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setEnableManagedSettings(true) - .setEnforceManagedModelDefaults(true).setManagedSettings(managedSettings), "managed-create"); + .setEnforceManagedModelDefaults(true).setInteractiveUser(true).setManagedSettings(managedSettings), + "managed-create"); var resume = SessionRequestBuilder.buildResumeRequest("managed-resume", new ResumeSessionConfig().setEnableManagedSettings(true).setEnforceManagedModelDefaults(true) - .setManagedSettings(managedSettings)); + .setInteractiveUser(true).setManagedSettings(managedSettings)); assertEquals(managedSettings, create.getManagedSettings()); assertEquals(managedSettings, resume.getManagedSettings()); assertTrue(create.getEnforceManagedModelDefaults()); assertTrue(resume.getEnforceManagedModelDefaults()); + assertEquals(Boolean.TRUE, create.getInteractiveUser()); + assertEquals(Boolean.TRUE, resume.getInteractiveUser()); var json = new ObjectMapper().writeValueAsString(create); assertTrue(json.contains("\"enableManagedSettings\":true")); assertTrue(json.contains("\"enforceManagedModelDefaults\":true")); + assertTrue(json.contains("\"interactiveUser\":true")); assertTrue(json.contains("\"managedSettings\":{\"permissions\"")); assertTrue(json.contains("\"disableBypassPermissionsMode\":\"disable\"")); assertTrue(json.contains("\"limitTo\":[\"Domain(github.com)\"]")); assertTrue(json.contains("\"disableAssistedPermissionsMode\":true")); } + @Test + void omitsInteractiveUserWhenUnset() throws Exception { + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "default-create"); + var resume = SessionRequestBuilder.buildResumeRequest("default-resume", new ResumeSessionConfig()); + var mapper = new ObjectMapper(); + + assertFalse(mapper.writeValueAsString(create).contains("interactiveUser")); + assertFalse(mapper.writeValueAsString(resume).contains("interactiveUser")); + } + @Test void acceptsFutureBypassPermissionsModes() throws Exception { var permissions = new ManagedSettingsPermissions().setDisableBypassPermissionsMode("future-fail-closed-mode"); 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/SessionHandlerTest.java b/java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java index 001489e41f..e77ca0adcb 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; @@ -21,6 +22,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.copilot.rpc.AgentStopHookOutput; import com.github.copilot.rpc.PermissionRequestResult; import com.github.copilot.rpc.PermissionRequestResultKind; @@ -47,6 +49,75 @@ public class SessionHandlerTest { private CopilotSession session; + @Test + void typedHookDispatchRetainsChildIdentityAndOldRootInputs() throws Exception { + var captured = new ArrayList(); + var hooks = new SessionHooks().setOnPreToolUse((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnPreMcpToolCall((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnPostToolUse((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnPostToolUseFailure((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnUserPromptSubmitted((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnUserPromptTransformed((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnSessionStart((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnSessionEnd((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnAgentStop((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnSubagentStart((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }).setOnSubagentStop((i, c) -> { + captured.add(i); + return CompletableFuture.completedFuture(null); + }); + session.registerHooks(hooks); + for (String hook : List.of("preToolUse", "preMcpToolCall", "postToolUse", "postToolUseFailure", + "userPromptSubmitted", "userPromptTransformed", "sessionStart", "sessionEnd", "agentStop", + "subagentStart", "subagentStop")) { + for (boolean enriched : List.of(true, false)) { + var input = MAPPER.createObjectNode().put("sessionId", "reported-session").put("timestamp", 1) + .put("cwd", "work"); + if (enriched) { + input.put("agentId", "child-actor").put("agentType", "explore").put("parentAgentId", + "parent-actor"); + } + if (hook.equals("sessionEnd")) { + input.putObject("error").put("message", "session-end-error"); + } + captured.clear(); + session.handleHooksInvoke(hook, input).get(); + assertEquals(1, captured.size(), hook); + JsonNode actual = MAPPER.valueToTree(captured.get(0)); + assertEquals("reported-session", actual.path("sessionId").asText(), hook); + if (hook.equals("sessionEnd")) { + assertEquals("session-end-error", actual.path("error").asText(), actual.toString()); + } + for (var field : Map + .of("agentId", "child-actor", "agentType", "explore", "parentAgentId", "parent-actor") + .entrySet()) { + assertEquals(enriched ? field.getValue() : "", actual.path(field.getKey()).asText(""), + hook + ": " + actual); + } + } + } + } + @BeforeEach void setup() throws Exception { var constructor = CopilotSession.class.getDeclaredConstructor(String.class, JsonRpcClient.class, String.class); @@ -291,6 +362,34 @@ void testHandleHooksInvokeSessionEnd() throws Exception { assertEquals("summary", output.sessionSummary()); } + @Test + void testHandleHooksInvokeSessionEndErrorShapes() throws Exception { + assertEquals("model timeout", sessionEndError(MAPPER.readTree("{\"message\":\"model timeout\"}"))); + assertEquals("boom", sessionEndError(MAPPER.readTree("\"boom\""))); + assertNull(sessionEndError(MAPPER.nullNode())); + assertNull(sessionEndError(null)); + assertEquals("{\"code\":7}", sessionEndError(MAPPER.readTree("{\"code\":7}"))); + } + + /** + * Runs a sessionEnd hook with the given wire {@code error} (absent when null) + * and returns the handler's error. + */ + private String sessionEndError(JsonNode error) throws Exception { + var captured = new AtomicReference("handler did not run"); + session.registerHooks(new SessionHooks().setOnSessionEnd((hookInput, invocation) -> { + captured.set(hookInput.error()); + return CompletableFuture.completedFuture(new SessionEndHookOutput(false, null, null)); + })); + ObjectNode input = MAPPER.createObjectNode().put("timestamp", 1735689600L).put("cwd", "/tmp").put("reason", + "error"); + if (error != null) { + input.set("error", error); + } + session.handleHooksInvoke("sessionEnd", input).get(); + return captured.get(); + } + // ===== handleHooksInvoke: agentStop ===== @Test 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..d28a6ee332 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; @@ -83,6 +85,38 @@ void transcriptRecoveryOverridesAndResponseInAllModes() throws Exception { assertNull(new ResumeSessionResponse("s1", null, null, null).transcriptRecovery()); } + @Test + void enableGitHubCacheStoreIsForwardedOnCreateAndResumeInAllModes() throws Exception { + var mapper = JsonRpcClient.getObjectMapper(); + for (var mode : new CopilotClientMode[]{CopilotClientMode.COPILOT_CLI, CopilotClientMode.EMPTY}) { + for (Boolean choice : new Boolean[]{null, false, true}) { + var create = new SessionConfig(); + var resume = new ResumeSessionConfig(); + if (choice != null) { + create.setEnableGitHubCacheStore(choice); + resume.setEnableGitHubCacheStore(choice); + } + var createJson = mapper.readTree( + mapper.writeValueAsBytes(SessionRequestBuilder.buildCreateRequest(create.clone(), "s1", mode))); + var resumeJson = mapper.readTree( + mapper.writeValueAsBytes(SessionRequestBuilder.buildResumeRequest("s1", resume.clone(), mode))); + for (var requestJson : List.of(createJson, resumeJson)) { + if (choice == null) { + assertFalse(requestJson.has("enableGitHubCacheStore")); + } else { + assertTrue(requestJson.path("enableGitHubCacheStore").isBoolean()); + assertEquals(choice.booleanValue(), requestJson.path("enableGitHubCacheStore").asBoolean()); + } + } + } + } + + var cleared = new SessionConfig().setEnableGitHubCacheStore(true).clearEnableGitHubCacheStore(); + assertTrue(cleared.getEnableGitHubCacheStore().isEmpty()); + var clearedResume = new ResumeSessionConfig().setEnableGitHubCacheStore(true).clearEnableGitHubCacheStore(); + assertTrue(clearedResume.getEnableGitHubCacheStore().isEmpty()); + } + @Test void clearingTranscriptRecoveryRestoresRuntimeDefaultInAllModes() throws Exception { var mapper = JsonRpcClient.getObjectMapper(); @@ -100,6 +134,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/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 73e0843c57..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 @@ -723,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()); @@ -735,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()); @@ -752,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 cf64fac0f7..c323700adf 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -375,11 +375,13 @@ Create a new conversation session. - `workingDirectory?: string` - Working directory for the session (default: runtime process cwd). - `refreshCustomInstructions?: boolean` - Invalidates the process-wide custom-instruction discovery cache before creating this session, so instruction-file edits made in the same runtime are read again. Defaults to `false` (cache reuse). Other sessions in this runtime may observe updated instructions on later turns or discovery. This does not watch files or enable disabled instruction loading. Available on `SessionConfig`, not `ResumeSessionConfig`. - `enableSessionStore?: boolean` - Enables the cross-session store for search and retrieval across sessions. When unset in `"copilot-cli"` mode, the runtime default applies (enabled). In `"empty"` mode, defaults to disabled. +- `enableGitHubCacheStore?: boolean` - Requests the read-only GitHub cache store (`github-cache-store` capability) on create or resume. The runtime registers the `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is also on. Defaults to disabled in every mode. - `gitHubTokenProvider?: GitHubTokenProvider` - Acquires rotating, session-scoped GitHub tokens. Token results require a positive `expiresIn` value in seconds remaining when the callback completes; production tokens typically last eight hours. Cannot be combined with `gitHubToken`. - `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section. - `onPermissionRequest?: PermissionHandler` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `approveAll` approves requests when managed settings are disabled and throws when `enableManagedSettings` is true. Custom handlers can inspect `managedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `onUserInputRequest?: UserInputHandler` - Handler for legacy question-and-answer requests from the agent. Enables the legacy `ask_user` tool. See [User Input Requests](#user-input-requests) section. - `askUserVariant?: "legacy" | "elicitation"` - Selects the model-facing `ask_user` tool shape when creating or cold-resuming a session. Defaults to `"legacy"`; use `"elicitation"` with `onElicitationRequest`. +- `interactiveUser?: boolean` - Declares that a person types this session's messages, as in an editor chat panel. Enables the runtime's user-message analysis telemetry for user-authored messages; automated messages stay excluded. Defaults to `false`. Ignored for cloud sessions. Re-supply it when cold-resuming a session; it is ignored when resuming a session that is already active. - `onElicitationRequest?: ElicitationHandler` - Handler for elicitation requests dispatched by the server. Enables this client to present form-based UI dialogs on behalf of the agent or other session participants. See [Elicitation Requests](#elicitation-requests) section. - `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. - `skillProvider?: SkillProvider` - **Experimental.** Serves skills from host storage instead of skill directories. Not persisted; pass it again on resume. See [Skill providers](#skill-providers-experimental). @@ -671,6 +673,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. @@ -1635,6 +1654,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/content-exclusion.ts b/nodejs/examples/content-exclusion.ts new file mode 100644 index 0000000000..24d51452c2 --- /dev/null +++ b/nodejs/examples/content-exclusion.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import type { CopilotClient } from "../src/index.js"; + +// Validate host-supplied policies before creating a session. +export async function validateContentExclusionPolicies(client: CopilotClient) { + const policiesJson = JSON.stringify([ + { + rules: [ + { + paths: ["secrets/**"], + source: { name: "Example", type: "Organization" }, + futureRuleField: { enabled: true }, + }, + ], + last_updated_at: "2026-10-05T12:00:00Z", + scope: "all", + futurePolicyField: { nullable: null }, + }, + ]); + const result = await client.rpc.contentExclusion.validatePolicies({ policiesJson }); + console.log("Validated content-exclusion policies:", JSON.stringify(result.policies)); + return result; +} 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 4e68a1b0eb..3ba56d21d8 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.96-2", + "copilotCliVersion": "1.0.98-1", "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 884f2a267e..31574d7664 100644 --- a/nodejs/src/cliVersion.ts +++ b/nodejs/src/cliVersion.ts @@ -1,3 +1,3 @@ -export const COPILOT_CLI_VERSION = "1.0.96-2"; +export const COPILOT_CLI_VERSION = "1.0.98-1"; export const COPILOT_CLI_USE_NPM_PACKAGE = false; diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index ed10b482b2..d728605e48 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, @@ -1869,9 +1870,12 @@ export class CopilotClient { enableFileHooks: config.enableFileHooks, enableHostGitOperations: config.enableHostGitOperations, enableSessionStore: config.enableSessionStore, + enableGitHubCacheStore: config.enableGitHubCacheStore, 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, @@ -1886,6 +1890,7 @@ export class CopilotClient { expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, enforceManagedModelDefaults: config.enforceManagedModelDefaults, + interactiveUser: config.interactiveUser, managedSettings: config.managedSettings, }); @@ -2161,6 +2166,7 @@ export class CopilotClient { enableFileHooks: config.enableFileHooks, enableHostGitOperations: config.enableHostGitOperations, enableSessionStore: config.enableSessionStore, + enableGitHubCacheStore: config.enableGitHubCacheStore, enableSkills: config.enableSkills, streaming: config.streaming, includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true, @@ -2178,6 +2184,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, @@ -2194,6 +2202,7 @@ export class CopilotClient { expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, enforceManagedModelDefaults: config.enforceManagedModelDefaults, + interactiveUser: config.interactiveUser, managedSettings: config.managedSettings, ...(extensionOptions?.requestedEnvironmentVariables ? { diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 68d1021d1f..55ca1a3ab2 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -34,6 +34,32 @@ export type AuthInfo = | AccountAuthInfo | GhCliAuthInfo | ApiKeyAuthInfo; +/** + * Authentication type + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AuthInfoType". + */ +/** @experimental */ +export type AuthInfoType = + /** Authentication provided by a GitHub App HMAC credential. */ + | "hmac" + /** Authentication resolved from environment-provided credentials. */ + | "env" + /** Authentication from an interactive user sign-in. */ + | "user" + /** Authentication from a selected provider-owned account, without a GitHub credential. */ + | "account" + /** Authentication delegated to the GitHub CLI. */ + | "gh-cli" + /** Authentication from an API key credential. */ + | "api-key" + /** Authentication from a GitHub token. */ + | "token" + /** Authentication from an SDK GitHub token callback. */ + | "token-provider" + /** Authentication from a Copilot API token. */ + | "copilot-api-token"; /** * The provider kind stamped on a signed-in account. * @@ -403,32 +429,6 @@ export type LoginProviderKind = | "proxima" /** Microsoft Entra sign-in that derives a GitHub (EMU) credential. */ | "entra"; -/** - * Authentication type - * - * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "AuthInfoType". - */ -/** @experimental */ -export type AuthInfoType = - /** Authentication provided by a GitHub App HMAC credential. */ - | "hmac" - /** Authentication resolved from environment-provided credentials. */ - | "env" - /** Authentication from an interactive user sign-in. */ - | "user" - /** Authentication from a selected provider-owned account, without a GitHub credential. */ - | "account" - /** Authentication delegated to the GitHub CLI. */ - | "gh-cli" - /** Authentication from an API key credential. */ - | "api-key" - /** Authentication from a GitHub token. */ - | "token" - /** Authentication from an SDK GitHub token callback. */ - | "token-provider" - /** Authentication from a Copilot API token. */ - | "copilot-api-token"; /** * 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). * @@ -622,7 +622,7 @@ export type CardDigestValue = string; /** @experimental */ export type CatalogAgentPluginCandidateKind = /** An Agent Plugin. */ "plugin"; /** - * Canonical Agent Plugin media type + * Media type naming the plugin format of an Agent Plugin candidate. Values other than `application/vnd.github.copilot-plugin` appear only when the caller requires the agent-plugin-formats capability. * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CatalogAgentPluginMediaType". @@ -630,7 +630,11 @@ export type CatalogAgentPluginCandidateKind = /** An Agent Plugin. */ "plugin"; /** @experimental */ export type CatalogAgentPluginMediaType = /** A GitHub Copilot Agent Plugin descriptor. */ - "application/vnd.github.copilot-plugin"; + | "application/vnd.github.copilot-plugin" + /** A Claude plugin descriptor (`.claude-plugin/plugin.json` layout). */ + | "application/vnd.anthropic.claude-plugin+json" + /** A portable Agent Plugin descriptor. */ + | "application/agent-plugin+json"; /** * Canonical catalogue resource identity. The runtime rewrites accepted urn:ai and urn:air identifiers to urn:air, lowercases the authority, and preserves the remaining resource components. * @@ -661,6 +665,83 @@ export type CatalogAgentPluginCompatibilityTag = | "canvas-only" /** The plugin targets GitHub Copilot. */ | "github-copilot"; +/** + * Typed installability state for an Agent Plugin candidate + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginInstallability". + */ +/** @experimental */ +export type CatalogAgentPluginInstallability = + /** The candidate carries complete, verified install data and the selected session may install it. Its install plan is present. */ + | "installable" + /** The catalogue supplied no complete, verified install data, so no install target is offered. */ + | "install-data-unavailable" + /** Plugin installation is understood but disabled for the selected session. */ + | "feature-disabled" + /** Reserved for plugin formats this runtime cannot install. Every format surfaced today installs through a marketplace. */ + | "not-installable-kind"; +/** + * One place an Agent Plugin can install from: Agent Finder's materialisation, a marketplace, or a GitHub repository. Each variant carries only its own fields. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginInstallSource". + */ +/** @experimental */ +export type CatalogAgentPluginInstallSource = + | CatalogAgentPluginMaterializationSource + | CatalogAgentPluginMarketplaceSource + | CatalogAgentPluginRepositorySource; +/** + * Discriminator for a materialisation install source + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginMaterializationSourceKind". + */ +/** @experimental */ +export type CatalogAgentPluginMaterializationSourceKind = + /** Install from Agent Finder's verified materialisation. */ + "materialization"; +/** + * Discriminator for a marketplace install source + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginMarketplaceSourceKind". + */ +/** @experimental */ +export type CatalogAgentPluginMarketplaceSourceKind = /** Install from a plugin marketplace. */ "marketplace"; +/** + * Discriminator for a repository install source + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginRepositorySourceKind". + */ +/** @experimental */ +export type CatalogAgentPluginRepositorySourceKind = /** Install directly from a GitHub repository. */ "repository"; +/** + * A kind of component an Agent Plugin contributes + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginComponent". + */ +/** @experimental */ +export type CatalogAgentPluginComponent = + /** Custom agents. */ + | "agent" + /** Slash commands. */ + | "command" + /** Skills. */ + | "skill" + /** Hooks. */ + | "hook" + /** Bundled MCP servers. */ + | "mcp-server" + /** Bundled LSP servers. */ + | "lsp-server" + /** Extensions. */ + | "extension" + /** Canvases. */ + | "canvas"; /** * 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. * @@ -912,6 +993,8 @@ export type CatalogCapability = | "ai-skill-discovery" /** Understands opt-in `application/vnd.github.copilot-plugin` candidates and their typed identity, version, source, and compatibility fields. */ | "agent-plugin-discovery" + /** Extends agent-plugin-discovery to Claude plugin (`application/vnd.anthropic.claude-plugin+json`) and portable Agent Plugin (`application/agent-plugin+json`) candidates, each reporting its format as its media type. Requires agent-plugin-discovery; without it, plugin searches return only Copilot plugins. */ + | "agent-plugin-formats" /** Understands side-effect-free MCP install-plan requests, results, and plan handles; `planning-unavailable` separately reports that planning is not enabled. */ | "mcp-install-planning" /** Understands plans that enumerate every eligible transport rather than a single preferred one. */ @@ -931,7 +1014,11 @@ export type CatalogCapability = /** Extends mcp-confirmed-remote-installation to declared non-secret header and URL values and receipt-owned header secrets for personal remote MCP choices. Requires mcp-confirmed-remote-installation and bound session authority. Secret values are written only to the reviewed backend after confirmation and are never returned; package and stdio choices remain unsupported. Advertised only when owned secret effects and owned secret activation are linked. */ | "mcp-configured-remote-installation" /** Understands verified Agent Finder Skill install, uninstall, recovery and installation-scoped enablement APIs. Advertised only by runtimes with the Skill installation engine linked; acquisition may still be refused as feature-disabled per selected session. */ - | "skill-confirmed-installation"; + | "skill-confirmed-installation" + /** Understands `installability` and the reviewable `installPlan` on Agent Plugin candidates. Requires agent-plugin-discovery; without it, plugin candidates are unchanged. A plugin is installable only with complete, verified catalogue install data, and may still be reported as feature-disabled per selected session. */ + | "agent-plugin-confirmed-installation" + /** Extends skill-confirmed-installation to planning from an Agent Finder materialisation reference through skills.planInstallFromReference, and to its distinct reference failure reasons. Requires skill-confirmed-installation. Advertised only by runtimes with the Skill installation engine linked. */ + | "skill-reference-installation"; /** * Bounded extensible wire-feature identifier. Known values are described by `CatalogCapability`; newer callers may send future identifiers so an older runtime can return a typed negotiation refusal instead of failing schema validation. Capability negotiation establishes contract understanding, while each operation's result separately reports runtime availability. * @@ -1055,7 +1142,11 @@ export type CatalogMediaType = /** An AI skill card. Representable and searchable, but typed non-installable. */ | "application/ai-skill" /** An inert Agent Plugin descriptor. */ - | "application/vnd.github.copilot-plugin"; + | "application/vnd.github.copilot-plugin" + /** An inert Claude plugin descriptor, surfaced only under the agent-plugin-formats capability. */ + | "application/vnd.anthropic.claude-plugin+json" + /** An inert portable Agent Plugin descriptor, surfaced only under the agent-plugin-formats capability. */ + | "application/agent-plugin+json"; /** * Why capability and protocol-version negotiation refused a caller * @@ -2030,6 +2121,33 @@ export type ExternalToolTextResultForLlmContentResourceLinkIconTheme = export type ExternalToolTextResultForLlmContentResourceDetails = | EmbeddedTextResourceContents | EmbeddedBlobResourceContents; +/** + * The population a feature flag is declared for, before overrides. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagAvailability". + */ +/** @experimental */ +/** @internal */ +export type FeatureFlagAvailability = + /** Enabled for everyone, before any override. */ + | "on" + /** Enabled only for accounts on the owning team. */ + | "team" + /** Enabled only for staff accounts that have also opted into experimental features. */ + | "staff-and-experimental" + /** Enabled for staff accounts and for accounts that opted into experimental features. */ + | "staff-or-experimental" + /** Enabled for any staff account, GitHub or Microsoft. */ + | "staff" + /** Enabled for GitHub staff accounts only. */ + | "staff-github" + /** Enabled for Microsoft staff accounts only. */ + | "staff-microsoft" + /** Enabled only for accounts that opted into experimental features. */ + | "experimental" + /** Disabled for everyone, before any override. */ + | "off"; /** * Content filtering mode to apply to all tools, or a map of tool name to content filtering mode. * @@ -2728,6 +2846,36 @@ export type ManagedPluginRetryStatus = | "deferred" /** The session's managed settings do not require this plugin, so nothing was done. */ | "not_required"; +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsBypassDisableSource". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsBypassDisableSource = + /** The user turned bypass mode off themselves. */ + | "user" + /** The account's server-delivered org policy disabled it. Pinned to the account named by `currentManagedIdentity`. */ + | "managed" + /** The device/MDM layer disabled it. Outranks the account, so no server answer lifts it. */ + | "mdm"; +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsBypassPolicyResolution". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsBypassPolicyResolution = + /** The policy resolved and does not disable bypass mode. */ + | "allowed" + /** The policy resolved and disables bypass mode. */ + | "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. */ + | "unknown"; /** * A channel accepted by managedSettings.compose. * @@ -2754,6 +2902,89 @@ export type ManagedSettingsDiagnosticSeverity = | "error" /** The runtime accepts the document but ignores the flagged content. */ | "warning"; +/** + * The operation that requires a policy-helper response. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsPolicyHelperReason". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsPolicyHelperReason = + /** Resolve policy for session startup. */ + | "sessionStart" + /** Resolve policy before plugin activation. */ + | "pluginActivation"; +/** + * How an ingestion may use the on-disk server-policy cache. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsServerPolicyCacheMode". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsServerPolicyCacheMode = + /** Serve a recent cache entry before a live fetch. */ + | "preferFresh" + /** Read cached policy without a network request. */ + | "cacheOnly" + /** Serve a cached entry only as a fallback after a failed live fetch. */ + | "fallbackOnly" + /** Ignore the cache entirely and rely on the live fetch alone. */ + | "disabled"; +/** + * How the server layer of an ingestion was resolved. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsServerPolicyResolution". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsServerPolicyResolution = + /** A live fetch succeeded. */ + | "live" + /** A recent cached entry was served without a live fetch. */ + | "freshCache" + /** No cached policy or confirmed no-policy result exists. */ + | "cacheMiss" + /** A live fetch failed and a cached entry was served in its place. */ + | "fallbackCache" + /** No policy could be resolved and the host proceeds unmanaged. */ + | "failOpen" + /** No policy could be resolved and the host applies the fail-closed gates. */ + | "failClosed" + /** No fetch was attempted because no usable token was available. */ + | "skippedNoToken"; +/** + * Which channel registered the policy helper that produced a helper layer. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsPolicyHelperAuthority". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsPolicyHelperAuthority = + /** The helper was registered by the device (MDM or managed file) layer. */ + | "device" + /** The helper was registered by the account's server-delivered policy. */ + | "server"; +/** + * Severity of a diagnostic emitted while ingesting managed settings. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsLogLevel". + */ +/** @experimental */ +/** @internal */ +export type ManagedSettingsLogLevel = + /** Detail useful only when diagnosing an ingestion. */ + | "debug" + /** Ordinary progress worth surfacing under a verbose flag. */ + | "info" + /** A condition the operator should see but which did not stop the ingestion. */ + | "warning" + /** A failure that changed the outcome of the ingestion. */ + | "error"; /** * UI theme preference per SEP-1865 * @@ -4533,6 +4764,36 @@ export type RemoteSessionMetadataTaskType = | "cca" /** CLI remote task. */ | "cli"; +/** + * Which repository settings file to edit. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "RepoSettingsEditScope". + */ +/** @experimental */ +/** @internal */ +export type RepoSettingsEditScope = + /** The shared repository settings file. */ + | "repo" + /** The local repository settings file. */ + | "local"; +/** + * One atomic settings mutation. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SettingsEditOperation". + */ +/** @experimental */ +/** @internal */ +export type SettingsEditOperation = + /** Set one scalar value. */ + | "set" + /** Add one string to a list, unless present. */ + | "add-item" + /** Remove one string from a list, unless absent. */ + | "remove-item" + /** Remove the setting without supplying a value. */ + | "remove"; /** * Provider-native structured output format. JSON Schema is forwarded without rewriting or validating the schema or the generated output. * @@ -4639,6 +4900,8 @@ export type SessionCapability = | "elicitation" /** Cross-session history tools and session-store SQL prompt/tool metadata. */ | "session-store" + /** Read-only GitHub cache SQL tool (github_sql). */ + | "github-cache-store" /** MCP Apps UI passthrough. */ | "mcp-apps" /** Host-provided canvas rendering support. */ @@ -5205,6 +5468,21 @@ export type SessionVisibilityStatus = | "repo" /** The session is restricted to its creator and collaborators. */ | "unshared"; +/** + * Whether one atomic settings edit changed a file. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SettingsEditStatus". + */ +/** @experimental */ +/** @internal */ +export type SettingsEditStatus = + /** The edit changed the setting and was stored. */ + | "changed" + /** The setting already had the requested state. */ + | "unchanged" + /** The existing settings file could not be edited safely; nothing was written. */ + | "invalid"; /** * Signal to send (default: SIGTERM) * @@ -5302,7 +5580,17 @@ export type SkillInstallationFailureReason = /** Installed Skill files no longer match ownership evidence. */ | "configuration-modified" /** A storage operation failed; inspect durable state before retrying. */ - | "write-failed"; + | "write-failed" + /** The materialisation reference is malformed or uses an unsupported schema version. Returned only to callers that require skill-reference-installation. */ + | "invalid-reference" + /** Agent Finder has no materialisation for the referenced resource revision (HTTP 404). Returned only to callers that require skill-reference-installation. */ + | "descriptor-not-found" + /** Agent Finder withdrew the referenced resource revision (HTTP 410). Returned only to callers that require skill-reference-installation. */ + | "descriptor-withdrawn" + /** The retrieved descriptor does not match the referenced descriptor or bundle digest. Returned only to callers that require skill-reference-installation. */ + | "digest-mismatch" + /** The referenced resource is not a Skill. Returned only to callers that require skill-reference-installation. */ + | "unsupported-resource"; /** * Management outcome for verified Skill inventory, planning and removal. * @@ -5595,6 +5883,38 @@ export type SlashCommandInvocationResult = | SlashCommandShowDialogResult | SlashCommandSetModelResult | SlashCommandSetPlanModelResult; +/** + * What the one-time staff log-level migration did. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "StaffLogLevelMigrationStatus". + */ +/** @experimental */ +export type StaffLogLevelMigrationStatus = + /** The account is not a staff account, so the migration does not apply and nothing was recorded. */ + | "ineligible" + /** The migration already ran; the stored completion timestamp is returned unchanged. */ + | "current" + /** The migration ran now. The persisted settings may have changed, so the host should adopt the returned settings when they are present. */ + | "refreshed"; +/** + * What the one-time staff update-channel migration did. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "StaffUpdateChannelMigrationStatus". + */ +/** @experimental */ +export type StaffUpdateChannelMigrationStatus = + /** The account is not a staff account, so the migration does not apply and nothing was recorded. */ + | "ineligible" + /** The migration already ran within the cutoff window; the stored completion timestamp is returned unchanged. */ + | "current" + /** The persisted update channel was changed to stable. Only this outcome requires the host to reload its configuration. */ + | "migrated" + /** The persisted channel was already stable, so nothing changed, but the migration is now recorded as done. */ + | "completed" + /** The account is on the experimental channel, which the migration deliberately leaves alone, and the migration is recorded as done. */ + | "excluded-experimental"; /** * Subagent settings to apply, or null to clear the live session override * @@ -5979,6 +6299,23 @@ export type UISessionLimitsExhaustedResponseAction = | "unset" /** Leave the limit unchanged and cancel the blocked model request. */ | "cancel"; +/** + * What the one-time obsolete-settings cleanup did to the document. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMigrateObsoleteOnDiskStatus". + */ +/** @experimental */ +/** @internal */ +export type UserSettingsMigrateObsoleteOnDiskStatus = + /** No settings document exists, so there was nothing to clean. */ + | "missing" + /** The document could not be parsed, so it was left untouched rather than rewritten from a partial read. */ + | "invalid" + /** The document parsed and held nothing obsolete, so nothing was written. */ + | "unchanged" + /** Obsolete keys were removed and the document was rewritten. */ + | "changed"; /** * Execution-critical workflow storage operation. * @@ -6231,6 +6568,62 @@ export type WorkspacesWorkspaceDetailsHostType = | "github" /** Workspace repository is hosted on Azure DevOps. */ | "ado"; +/** + * True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxProxyCaCheckInstallationSupportResult". + */ +/** @experimental */ +export type SandboxProxyCaCheckInstallationSupportResult = boolean; +/** + * True when the sandbox configuration uses the credential proxy and can need certificate-authority setup. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxProxyCaCheckRelevanceResult". + */ +/** @experimental */ +export type SandboxProxyCaCheckRelevanceResult = boolean; +/** + * True when an OS-level sandbox can run on this host. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxCheckPlatformSupportResult". + */ +/** @experimental */ +export type SandboxCheckPlatformSupportResult = boolean; +/** + * True when the sandbox on this host can reach host loopback directly. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxCheckWindowsHostLoopbackSupportResult". + */ +/** @experimental */ +export type SandboxCheckWindowsHostLoopbackSupportResult = boolean; +/** + * The user-facing refusal, including the platform remedy and the documentation link. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxGetUnsupportedMessageResult". + */ +/** @experimental */ +export type SandboxGetUnsupportedMessageResult = string; +/** + * The user-facing notice that a saved sandbox setting is inert for this session. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxGetUnsupportedSettingDisabledNoticeResult". + */ +/** @experimental */ +export type SandboxGetUnsupportedSettingDisabledNoticeResult = string; +/** + * True when the command stays available while enforcement is blocked. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxCheckEnforcementCommandResult". + */ +/** @experimental */ +export type SandboxCheckEnforcementCommandResult = boolean; /** * List of all authenticated users * @@ -6944,6 +7337,62 @@ export interface ApiKeyAuthInfo { host: string; copilotUser?: CopilotUserResponse; } +/** + * The authentication identity whose shared wording should be rendered. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountDescribeAuthInfoRequest". + */ +/** @experimental */ +/** @internal */ +export interface AccountDescribeAuthInfoRequest { + identity: AuthIdentity; +} +/** + * Credential-free authentication identity safe to expose to hosts and user interfaces. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AuthIdentity". + */ +/** @experimental */ +export interface AuthIdentity { + type: AuthInfoType; + /** + * Authentication host + */ + host: string; + /** + * Authenticated login, when available + */ + login?: string; + /** + * Name of the environment variable that supplied the credential, when applicable + */ + envVar?: string; + /** + * Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. + */ + registrationId?: string; + copilotUser?: CopilotUserResponse; +} +/** + * The wording the runtime uses for an authentication identity. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountDescribeAuthInfoResult". + */ +/** @experimental */ +/** @internal */ +export interface AccountDescribeAuthInfoResult { + /** + * Sign-in banner announcing the authenticated user. + */ + message: string; + /** + * One-line description of the identity, suitable after a verb such as "Switched to". + */ + description: string; +} /** * Current authentication state * @@ -6958,6 +7407,39 @@ export interface AccountGetCurrentAuthResult { */ authErrors?: string[]; } +/** + * The authentication identity whose gist support should be reported. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountGetGistSupportRequest". + */ +/** @experimental */ +/** @internal */ +export interface AccountGetGistSupportRequest { + identity: AuthIdentity; +} +/** + * Whether the identity can create gists, and why not when it cannot. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountGetGistSupportResult". + */ +/** @experimental */ +/** @internal */ +export interface AccountGetGistSupportResult { + /** + * True when the identity can create gists. + */ + supported: boolean; + /** + * Machine-readable reason category when unsupported. + */ + kind?: string; + /** + * Human-readable explanation to show where the gist action would have been. + */ + reason?: string; +} /** @experimental */ export interface AccountGetQuotaRequest { @@ -7026,6 +7508,49 @@ export interface AccountQuotaSnapshot { */ resetDate?: string; } +/** + * The environment variable supplying a classic personal access token, when one is set. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountGetTokenEnvVarResult". + */ +/** @experimental */ +/** @internal */ +export interface AccountGetTokenEnvVarResult { + /** + * Variable name, for example `GH_TOKEN`. Absent when no classic token is present in the environment. + */ + name?: string; +} +/** + * A path to resolve against the GitHub host an identity is signed in to. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountGetWebUrlRequest". + */ +/** @experimental */ +/** @internal */ +export interface AccountGetWebUrlRequest { + /** + * Host-relative path, without a leading slash, for example `settings/copilot/features`. + */ + path: string; + identity?: AuthIdentity; +} +/** + * An absolute URL on the identity's GitHub host. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AccountGetWebUrlResult". + */ +/** @experimental */ +/** @internal */ +export interface AccountGetWebUrlResult { + /** + * Absolute URL for the requested path. + */ + url: string; +} /** * Credentials to validate and store. Omit login to resolve the authenticated user from the token. * @@ -7255,6 +7780,12 @@ export interface AgentInfo { * 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. */ prompt?: string; + /** + * Declarative hook actions keyed by event name, executed only while this agent is active. + */ + hooks?: { + [k: string]: JsonValue[] | undefined; + }; } /** * Agents available to the session. @@ -7534,6 +8065,10 @@ export interface AgentsCustomAgentInitialModelDecisionResult { */ /** @experimental */ export interface AgentsDiscoverRequest { + /** + * 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[]; /** * 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). */ @@ -7727,6 +8262,10 @@ export interface AgentsGetBuiltinsResult { */ /** @experimental */ export interface AgentsGetDiscoveryPathsRequest { + /** + * 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[]; /** * Optional list of project directory paths. When omitted or empty, only the user-level directory is returned. */ @@ -7754,33 +8293,6 @@ export interface ProviderDescriptor { */ available: boolean; } -/** - * Credential-free authentication identity safe to expose to hosts and user interfaces. - * - * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "AuthIdentity". - */ -/** @experimental */ -export interface AuthIdentity { - type: AuthInfoType; - /** - * Authentication host - */ - host: string; - /** - * Authenticated login, when available - */ - login?: string; - /** - * Name of the environment variable that supplied the credential, when applicable - */ - envVar?: string; - /** - * Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. - */ - registrationId?: string; - copilotUser?: CopilotUserResponse; -} /** * Credential-free identity metadata. * @@ -8660,6 +9172,8 @@ export interface CatalogAgentPluginCandidate { | [CatalogAgentPluginCompatibilityTag] | [CatalogAgentPluginCompatibilityTag, CatalogAgentPluginCompatibilityTag] | [CatalogAgentPluginCompatibilityTag, CatalogAgentPluginCompatibilityTag, CatalogAgentPluginCompatibilityTag]; + installability?: CatalogAgentPluginInstallability; + installPlan?: CatalogAgentPluginInstallPlan; provenance: CatalogAgentPluginCandidateProvenance; trust?: CatalogTrustSnapshot; } @@ -8680,6 +9194,102 @@ export interface CatalogPluginRepositorySource { */ path: string; } +/** + * What installing an Agent Plugin would do, for the user to review and confirm. Provisional: the catalogue install data it is built from is not final. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginInstallPlan". + */ +/** @experimental */ +export interface CatalogAgentPluginInstallPlan { + /** + * Every valid install source, in preference order: materialization, then marketplace, then repository. Each kind appears at most once. + * + * @minItems 1 + * @maxItems 3 + */ + sources: + | [CatalogAgentPluginInstallSource] + | [CatalogAgentPluginInstallSource, CatalogAgentPluginInstallSource] + | [CatalogAgentPluginInstallSource, CatalogAgentPluginInstallSource, CatalogAgentPluginInstallSource]; + /** + * The plugin's name, as named in its marketplace or manifest. + */ + pluginName: string; + version?: CatalogResourceVersion; + /** + * Exact full commit ID to install, in lowercase hex. Always present when the plan has a repository source; a plan with a marketplace source carries at least one of version and commit. + */ + commit?: string; + /** + * Components the plugin contributes, in canonical order. + * + * @minItems 1 + */ + components: [CatalogAgentPluginComponent, ...CatalogAgentPluginComponent[]]; + /** + * Canvas IDs the plugin contributes. Non-empty exactly when components include `canvas`. + */ + canvasIds: string[]; + /** + * Extension IDs the plugin contributes. Non-empty exactly when components include `extension`. + */ + extensionIds: string[]; + /** + * MCP servers the plugin depends on but does not bundle. + */ + mcpDependencies: string[]; + /** + * Connectors the plugin depends on. + */ + connectorDependencies: string[]; +} +/** + * Agent Finder's verified materialisation of the plugin. Present only when the candidate carries a complete materialisation reference; the reference itself stays runtime-private. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginMaterializationSource". + */ +/** @experimental */ +export interface CatalogAgentPluginMaterializationSource { + kind: CatalogAgentPluginMaterializationSourceKind; +} +/** + * A plugin marketplace named by verified catalogue install data, installed as `pluginName@name`. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginMarketplaceSource". + */ +/** @experimental */ +export interface CatalogAgentPluginMarketplaceSource { + kind: CatalogAgentPluginMarketplaceSourceKind; + /** + * Marketplace name. + */ + name: string; + /** + * Canonical lowercase GitHub owner/repository hosting the marketplace. + */ + repository: string; +} +/** + * A GitHub repository holding the plugin, installed directly as `owner/repo[:path]` at the plan's commit, without a marketplace. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogAgentPluginRepositorySource". + */ +/** @experimental */ +export interface CatalogAgentPluginRepositorySource { + kind: CatalogAgentPluginRepositorySourceKind; + /** + * Canonical lowercase GitHub owner/repository holding the plugin. + */ + repository: string; + /** + * Relative POSIX path of the plugin directory in the repository. Omitted when the plugin is at the repository root. + */ + path?: string; +} /** * Where and when an Agent Plugin catalog reference was observed. Discovery provenance deliberately carries no descriptor URL, raw data, candidate handle, or content digest. * @@ -9051,6 +9661,39 @@ export interface CatalogMalformedCardError { */ message: string; } +/** + * An Agent Finder materialisation reference pinning one exact resource revision. + * + * It carries no origin or URL: the runtime always builds the descriptor URL on its fixed Agent + * Finder authority. It carries no display data either; every reviewed field comes from the + * verified descriptor and Skill files. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CatalogMaterializationReference". + */ +/** @experimental */ +export interface CatalogMaterializationReference { + /** + * Agent Finder resource identifier, a positive decimal integer. + */ + resourceId: string; + /** + * Agent Finder materialisation revision identifier, a positive decimal integer. This is not a Git commit. + */ + revisionId: string; + /** + * Materialisation descriptor schema version, currently `1.0.0`. + */ + schemaVersion: string; + /** + * Expected `sha256:` digest of the canonical descriptor. + */ + descriptorDigest: string; + /** + * Expected `sha256:` digest of the descriptor's bundle manifest. + */ + bundleDigest: string; +} /** * The protocol version and capability set the runtime actually honoured for a successful catalog operation. * @@ -10519,6 +11162,32 @@ export interface ContentExclusionPathCheck { */ excluded: boolean; } +/** + * Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ContentExclusionValidatePoliciesRequest". + */ +/** @experimental */ +export interface ContentExclusionValidatePoliciesRequest { + /** + * Unparsed JSON text expected to hold an array of content-exclusion policies. + */ + policiesJson: string; +} +/** + * The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this result. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ContentExclusionValidatePoliciesResult". + */ +/** @experimental */ +export interface ContentExclusionValidatePoliciesResult { + /** + * Validated policy objects in input order. Raw JSON preserves every field returned by the runtime's policy parser. + */ + policies: JsonValue[]; +} /** * A single large message currently in context. * @@ -12021,6 +12690,161 @@ export interface ExternalToolTextResultForLlmContentResource { type: "resource"; resource: ExternalToolTextResultForLlmContentResourceDetails; } +/** + * The declared shape of one feature flag, independent of any caller population. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagMetadata". + */ +/** @experimental */ +/** @internal */ +export interface FeatureFlagMetadata { + /** + * The flag's name, matching the key it is listed under. + */ + name: string; + availability: FeatureFlagAvailability; + /** + * Whether the flag participates in the CAPI sanity suite. + */ + capiSanity: boolean; + /** + * 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; + /** + * The online-experiment (ExP) flag this maps to, when the flag is assigned by an experiment rather than declared statically. + */ + expFlag?: string; + /** + * 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; +} +/** + * Every feature flag this runtime declares, with the shape each one was declared with. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagsMetadataResult". + */ +/** @experimental */ +/** @internal */ +export interface FeatureFlagsMetadataResult { + /** + * Declared feature flags keyed by flag name. + */ + flags: { + [k: string]: FeatureFlagMetadata; + }; +} +/** + * Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagsResolveRequest". + */ +/** @experimental */ +/** @internal */ +export interface FeatureFlagsResolveRequest { + /** + * Whether the caller is GitHub or Microsoft staff. + */ + isStaff: boolean; + /** + * Whether the caller is in an experimental population. + */ + isExperimental: boolean; + /** + * Whether the caller belongs to the team population used by feature availability. + */ + isTeam: boolean; + /** + * 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?: boolean; + /** + * Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + */ + isStaffMicrosoft?: boolean; + /** + * Resolve against this already-computed flag set rather than each flag's declared availability. Keyed by flag name. + */ + baseFlags?: { + [k: string]: boolean | undefined; + }; + /** + * 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?: { + [k: string]: string | undefined; + }; + /** + * User settings to apply, including `enabledFeatureFlags`, legacy `feature_flags.enabled`, and supported convenience settings. + */ + config?: JsonValue; + /** + * Flags forced to a value after every other layer, keyed by flag name. + */ + flagOverrides?: { + [k: string]: boolean | undefined; + }; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FeatureFlagsResolveStartupGatesRequest". + */ +/** @experimental */ +/** @internal */ +export interface FeatureFlagsResolveStartupGatesRequest { + /** + * 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[]; + /** + * Whether the caller is GitHub or Microsoft staff. + */ + isStaff: boolean; + /** + * Whether the caller is in an experimental population. + */ + isExperimental: boolean; + /** + * Whether the caller belongs to a team plan. + */ + isTeam: boolean; + /** + * 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?: boolean; + /** + * Microsoft-staff membership. Omitted the same way as `isStaffGitHub`. + */ + isStaffMicrosoft?: boolean; + /** + * Environment to read flag overrides from. Defaults to this runtime process's own environment. + */ + environment?: { + [k: string]: string | undefined; + }; + /** + * User settings document whose `featureFlags` block is layered on top of the resolved set. + */ + config?: JsonValue; + /** + * Whether streamer mode is on, which withholds the populations an experiment would otherwise segment on. + */ + streamerMode: boolean; + /** + * 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?: number; + /** + * 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: boolean; +} /** * Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. * @@ -12100,6 +12924,62 @@ export interface FolderTrustCheckResult { */ trusted: boolean; } +/** + * A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "FolderTrustIsTrustedParams". + */ +/** @experimental */ +/** @internal */ +export interface FolderTrustIsTrustedParams { + /** + * 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; + /** + * 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; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ForceLoginOrgsDecision". + */ +/** @experimental */ +/** @internal */ +export interface ForceLoginOrgsDecision { + /** + * Whether the client may proceed. True whenever no pin is in force, so an unmanaged host is never blocked. + */ + allowed: boolean; + /** + * 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: boolean; + /** + * 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[]; + /** + * The approved organization the account matched, when allowed under a pin. + */ + matchedOrg?: string; + /** + * 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; + /** + * A developer-facing explanation of the refusal. Omitted when allowed. + */ + message?: string; + /** + * 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[]; +} /** * The remote the checked-out branch tracks. * @@ -13141,6 +14021,10 @@ export interface HookInvokeResponse { */ /** @experimental */ export interface HooksDiscoverRequest { + /** + * 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[]; /** * 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. */ @@ -14225,6 +15109,40 @@ export interface InstructionDiscoveryPathList { */ paths: InstructionDiscoveryPath[]; } +/** + * Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "InstructionsDiscoverRepoSourcesRequest". + */ +/** @experimental */ +/** @internal */ +export interface InstructionsDiscoverRepoSourcesRequest { + /** + * Repository location to discover. An empty string selects no repository scope; cwdPath can still supply working-directory sources. + */ + location: string; + /** + * When true, include the sources a command-line host reads. Hosts that are not the CLI omit it. + */ + isCli?: boolean; + /** + * Working directory whose instruction sources should also be included. Omission adds no separate working-directory scope. + */ + cwdPath?: string; + /** + * Copilot configuration directory for user-level sources. An omitted or empty value uses the server's configuration home. + */ + configDir?: string; + /** + * Extra directories to scan alongside instruction directories configured in the runtime environment. + */ + additionalDirs?: string[]; + /** + * When true, also discover instruction files nested below the location. + */ + enableChildInstructions?: boolean; +} /** * Optional project paths to include in instruction discovery. * @@ -14233,6 +15151,10 @@ export interface InstructionDiscoveryPathList { */ /** @experimental */ export interface InstructionsDiscoverRequest { + /** + * 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[]; /** * 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). */ @@ -14250,6 +15172,10 @@ export interface InstructionsDiscoverRequest { */ /** @experimental */ export interface InstructionsGetDiscoveryPathsRequest { + /** + * 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[]; /** * Optional list of project directory paths. When omitted or empty, only the user-level targets are returned. */ @@ -14315,6 +15241,20 @@ export interface InstructionSource { */ projectPath?: string; } +/** + * The prompt the init command runs. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "InstructionsInitPromptResult". + */ +/** @experimental */ +/** @internal */ +export interface InstructionsInitPromptResult { + /** + * Prompt text authoring repository instructions. + */ + prompt: string; +} /** * Parameters for interrupting the main agent turn. * @@ -14648,6 +15588,20 @@ export interface SessionContext { */ branch?: string; } +/** + * Which stored location permissions were dropped because their directory is gone. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LocationPermissionsCleanupResult". + */ +/** @experimental */ +/** @internal */ +export interface LocationPermissionsCleanupResult { + /** + * Location keys whose grants were removed. Empty when every stored location still exists. + */ + removedLocationKeys: string[]; +} /** * Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip. * @@ -14817,6 +15771,38 @@ export interface ManagedSettingMeta { */ requested?: string; } +/** + * A user settings document and the effective managed policy whose controlled entries should be stamped onto it. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsApplyControlsParams". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsApplyControlsParams { + /** + * The user settings document to stamp. Entries the managed layer does not name are returned exactly as supplied. + */ + settings: JsonValue; + /** + * The effective managed policy, as composed by `managedSettings.resolveEffectiveValue`. Omit it on an unmanaged host: the settings then come back unchanged. + */ + managed?: JsonValue; +} +/** + * The user settings with the managed layer's controlled entries stamped on. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsApplyControlsResult". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsApplyControlsResult { + /** + * The resulting settings document. + */ + settingsJson: JsonValue; +} /** * One candidate channel; absent settings represents a channel that delivered no document. * @@ -14931,6 +15917,308 @@ export interface ManagedSettingsDiagnostic { */ message: string; } +/** + * Inputs describing the host and account an ingestion should resolve managed policy for. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsIngestParams". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsIngestParams { + policyHelperReason?: ManagedSettingsPolicyHelperReason; + /** + * Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + */ + selfFetch?: boolean; + /** + * The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + */ + authInfo?: JsonValue; + /** + * 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; + /** + * 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?: JsonValue; + /** + * 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?: boolean; + /** + * Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + */ + bypassCacheRead?: boolean; + serverPolicyCacheMode?: ManagedSettingsServerPolicyCacheMode; + /** + * Whether a configured platform policy helper may be run to produce an enforced helper layer. + */ + policyHelperEnabled?: boolean; + /** + * 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; + /** + * User agent to send with the policy fetch. + */ + userAgent: string; + /** + * 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; + /** + * 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: JsonValue; + /** + * The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + */ + platform: string; + /** + * The host's home directory, used to resolve cache and managed-file locations. + */ + homeDirectory: 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsIngestResult". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsIngestResult { + /** + * The effective managed settings composed from every resolved layer. Omitted when no layer set anything, so an unmanaged host locks nothing. + */ + settings?: JsonValue; + /** + * The effective managed permissions layer, when the resolved policy carries one. + */ + permissions?: JsonValue; + /** + * The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. + */ + serverFetchFailed: boolean; + /** + * No usable token was available, so no server fetch was attempted. + */ + serverTokenUnavailable: boolean; + /** + * The error that made the server fetch fail, when one did. + */ + serverFetchError?: string; + serverResolution: ManagedSettingsServerPolicyResolution; + /** + * The server fetch failed in a way a later retry could succeed at, as opposed to a refusal that no retry will change. + */ + serverRetryableFailure: boolean; + /** + * 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: boolean; + /** + * 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: boolean; + /** + * The account's server-delivered policy, when one was resolved. + */ + serverResponse?: JsonValue; + /** + * 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?: JsonValue; + /** + * 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: boolean; + /** + * 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; + /** + * Minimum client version requested by a successful response. Advisory only; the delivered policy remains active. + */ + serverClientUpdateRequested?: string; + /** + * The synthesized fail-closed permissions layer applied in place of a policy a refused client may not enforce. + */ + serverPermissionsFloor?: JsonValue; + /** + * The device layer discovered from the machine, when one exists. + */ + deviceResponse?: JsonValue; + /** + * The device layer could not be read, so it is unconfirmed rather than known-absent. + */ + deviceLoadFailed: boolean; + /** + * The sandbox floor contributed by the device layer. + */ + deviceSandboxFloor?: JsonValue; + /** + * A device-managed policy is known to exist. + */ + deviceSourceFound: boolean; + /** + * 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: boolean; + /** + * The layer produced by the platform policy helper, when one ran. + */ + policyHelperResponse?: JsonValue; + policyHelperAuthority?: ManagedSettingsPolicyHelperAuthority; + /** + * The policy helper was configured but could not be run or its output could not be read. + */ + policyHelperLoadFailed: boolean; + /** + * A policy helper is configured for this host. + */ + policyHelperConfigured: boolean; + /** + * The policy helper's failure must be treated as fail-closed rather than ignored. + */ + policyHelperFailClosed: boolean; + /** + * Diagnostics emitted while resolving the layers, in the order they occurred. + */ + logMessages: 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsLogMessage". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsLogMessage { + level: ManagedSettingsLogLevel; + /** + * Human-readable diagnostic text. + */ + message: string; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsLoadRemoteControlParams". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsLoadRemoteControlParams { + policyHelperReason?: ManagedSettingsPolicyHelperReason; + /** + * Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + */ + selfFetch?: boolean; + /** + * The authenticated identity to resolve policy for. Omitted before sign-in, in which case no server layer is fetched. + */ + authInfo?: JsonValue; + /** + * 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?: JsonValue; + /** + * 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?: boolean; + /** + * Whether a configured platform policy helper may be run to produce an enforced helper layer. + */ + policyHelperEnabled?: boolean; + /** + * User agent to send with the policy fetch. + */ + userAgent: string; + /** + * 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; + /** + * The host's environment variables, read for cache location and policy overrides. + */ + environment: JsonValue; + /** + * The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + */ + platform: string; + /** + * The host's home directory, used to resolve cache and managed-file locations. + */ + homeDirectory: 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsLoadRemoteControlResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + remoteControl?: JsonValue; +} +/** + * The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsParseClientValueParams". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + settings?: JsonValue; +} +/** + * The accepted managed settings, reduced to the keys a client is allowed to set. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsParseClientValueResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + settingsJson?: JsonValue; +} +/** + * The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsReadDeviceResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + settings?: JsonValue; + /** + * Sandbox floor read from the discovered document before validation, so a document that is otherwise rejected can still raise the floor it declared. + */ + sandboxFloor?: JsonValue; + /** + * 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: boolean; +} /** * Validated device-managed settings discovered before a session exists. * @@ -14948,6 +16236,107 @@ export interface ManagedSettingsReadResult { */ errorMessage?: string; } +/** + * The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveBypassDisableTransitionParams". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsResolveBypassDisableTransitionParams { + currentSource?: ManagedSettingsBypassDisableSource; + /** + * 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; + /** + * The account signed in now. A `managed` disable pinned to a different account is lifted. + */ + activeIdentity: string; + policy: ManagedSettingsBypassPolicyResolution; +} +/** + * The bypass-permissions state a host should hold after the resolution, and what it must do to get there. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveBypassDisableTransitionResult". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsResolveBypassDisableTransitionResult { + source?: ManagedSettingsBypassDisableSource; + /** + * 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; + /** + * 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?: boolean; + /** + * 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: boolean; +} +/** + * The credential and environment to evaluate the device-channel sign-in pin against. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveDeviceForceLoginOrgsGateParams". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + authInfo?: JsonValue; + /** + * 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: JsonValue; +} +/** + * The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveEffectiveValueParams". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + server?: JsonValue; + /** + * The device layer: native MDM and managed-file settings describing the machine rather than the account. + */ + mdm?: JsonValue; + /** + * The device sandbox floor, folded into the device layer under `sandbox` before composition. + */ + deviceSandboxFloor?: JsonValue; + /** + * The policy-helper layer, an enforced channel sourced from a platform policy helper process. + */ + policyHelper?: JsonValue; +} +/** + * The effective managed policy composed from the supplied layers. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveEffectiveValueResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + settingsJson?: JsonValue; +} /** @experimental */ export interface ManagedSettingsResolveRequest { @@ -14993,6 +16382,224 @@ export interface ManagedSettingsResolveResult { */ diagnostics: ManagedSettingsDiagnostic[]; } +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveStartupPolicyParams". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsResolveStartupPolicyParams { + policyHelperReason?: ManagedSettingsPolicyHelperReason; + /** + * Whether to run ingestion. Omit or set false to return an empty result without discovering or fetching policy. + */ + selfFetch?: boolean; + /** + * 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?: JsonValue; + /** + * 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; + /** + * 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?: JsonValue; + /** + * 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?: boolean; + /** + * Whether to ignore a cached server policy when reading, forcing the resolution to come from a live fetch. + */ + bypassCacheRead?: boolean; + serverPolicyCacheMode?: ManagedSettingsServerPolicyCacheMode; + /** + * Whether a configured platform policy helper may be run to produce an enforced helper layer. + */ + policyHelperEnabled?: boolean; + /** + * 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; + /** + * User agent to send with the policy fetch. + */ + userAgent: string; + /** + * 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; + /** + * The host's environment variables, read for cache location, policy overrides, and the variables that would make a membership snapshot forgeable. + */ + environment: JsonValue; + /** + * The host's platform, in Node's `process.platform` spelling, used to locate platform-specific cache and policy paths. + */ + platform: string; + /** + * The host's home directory, used to resolve cache and managed-file locations. + */ + homeDirectory: string; + /** + * 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: boolean; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsResolveStartupPolicyResult". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsResolveStartupPolicyResult { + serverResolution: ManagedSettingsServerPolicyResolution; + /** + * Advisory minimum client version from a successful server response. + */ + clientUpdateRequested?: string; + /** + * The account's server-delivered policy, when one was resolved. + */ + serverResponse?: JsonValue; + /** + * The device layer discovered from the machine, when one exists. + */ + deviceResponse?: JsonValue; + /** + * The sandbox floor contributed by the device layer. + */ + deviceSandboxFloor?: JsonValue; + /** + * The layer produced by the platform policy helper, when one ran. + */ + policyHelperResponse?: JsonValue; + /** + * The server fetch failed and no cached entry stood in for it, so the server layer is unconfirmed and fail-closed gates apply. + */ + serverFetchFailed: boolean; + /** + * The device layer could not be read, so it is unconfirmed rather than known-absent. + */ + deviceLoadFailed: boolean; + /** + * The policy helper was configured but could not be run, or its output could not be read. + */ + policyHelperLoadFailed: boolean; + /** + * Telemetry describing which layers answered and how, for the host to report alongside its own startup events. + */ + telemetry: JsonValue; + /** + * The effective sandbox floor composed across every layer, which a host must apply rather than its own default. + */ + sandboxFloor?: JsonValue; + /** + * 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: boolean; + /** + * The permissions layer specifically could not be confirmed, so permission gates apply their fail-closed posture. + */ + permissionPolicyUndetermined: boolean; + /** + * A managed policy is known to apply to this host, from a discovered source or a resolved document. + */ + managedPolicyPresent: boolean; + /** + * 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: boolean; + forceLoginOrgs: ForceLoginOrgsDecision; + /** + * 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; + /** + * Warnings raised while resolving, in the order they occurred. + */ + warnings: string[]; +} +/** + * The managed layers to retain as this process's startup policy, with the outcome of fetching each one. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsRetainPolicyParams". + */ +/** @experimental */ +/** @internal */ +export interface ManagedSettingsRetainPolicyParams { + /** + * The resolved server policy. A known source can lack a response after a forced-refresh failure. + */ + serverResponse?: JsonValue; + /** + * The device/MDM policy layer. + */ + deviceResponse?: JsonValue; + /** + * Sandbox floor declared by the device layer, carried separately so a document that failed validation can still raise the floor. + */ + deviceSandboxFloor?: JsonValue; + /** + * 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: boolean; + /** + * Whether any managed policy source exists, independently of fetch failures. + */ + managedPolicyPresent: boolean; + /** + * Whether the server policy source exists, even when its response is unavailable. + */ + serverSourceFound: boolean; + /** + * Whether a required fresh server-policy confirmation failed. + */ + serverForcedRefreshFailure: boolean; + /** + * Preserve the retained server layer during a device-only resolution. + */ + preserveServerState: boolean; + /** + * Whether the device sandbox intent is unreadable independently of its other settings. + */ + deviceSandboxUndetermined: boolean; + /** + * Whether the shared sandbox policy is undetermined. Diagnostic errors alone do not imply this flag. + */ + sandboxFloorUndetermined: boolean; + /** + * Whether permission policy is undetermined. Sandbox-only uncertainty does not imply this flag. + */ + permissionPolicyUndetermined: boolean; + /** + * Whether uncertainty alone enabled the shared sandbox floor. + */ + sandboxEnabledByUndeterminedPolicy: boolean; + /** + * Shared server/device sandbox floor, excluding session-local helper policy. + */ + sharedSandboxFloor?: JsonValue; + /** + * Whether the server fetch failed. Report it honestly: true keeps the already-retained server layer rather than replacing it with an absent one. + */ + serverFetchFailed: boolean; + /** + * Whether the device load failed. Report it honestly, for the same reason as `serverFetchFailed`. + */ + deviceLoadFailed: boolean; + /** + * 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?: JsonValue; +} /** * The authoring JSON schema for managed settings recognized by this runtime. * @@ -16369,6 +17976,20 @@ export interface McpIsServerRunningResult { */ running: boolean; } +/** + * Names of the MCP servers the runtime supplies itself. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpListBuiltinServerNamesResult". + */ +/** @experimental */ +/** @internal */ +export interface McpListBuiltinServerNamesResult { + /** + * Every built-in server name. A host disabling the built-ins wholesale suppresses exactly this set. + */ + names: string[]; +} /** * Server name whose tool list should be returned. * @@ -18500,6 +20121,64 @@ export interface ModelClearStartupSeedRequest { export interface ModelClearStartupSeedResult { cleared: boolean; } +/** + * Host presentation state the reasoning-summaries policy depends on. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ModelGetReasoningSummariesRequest". + */ +/** @experimental */ +export interface ModelGetReasoningSummariesRequest { + /** + * The host's persisted `showReasoning` user setting, omitted when unset. + */ + persistedShowReasoning?: boolean; + /** + * Whether the host is currently showing reasoning summaries. Defaults to false. + */ + summariesVisible?: boolean; +} +/** + * Reasoning-summaries presentation policy for the session's current model. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ModelGetReasoningSummariesResult". + */ +/** @experimental */ +export interface ModelGetReasoningSummariesResult { + /** + * 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: boolean; + /** + * 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: boolean; + /** + * Whether turns on this session produce reasoning summaries, honouring an explicit session reasoning-summary override. + */ + sessionShowsSummaries: boolean; + /** + * Whether the selected model shows reasoning summaries by default. + */ + modelDefaultShowsSummaries: boolean; + /** + * Whether the selected model emits reasoning headers. + */ + showReasoningHeaders: boolean; + /** + * 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?: boolean; + /** + * Whether to show the hint that reasoning summaries are hidden. Always false while `experimentSettled` is false. + */ + showHiddenHint: boolean; + /** + * 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: boolean; +} /** * List of Copilot models available to the resolved user, including capabilities and billing metadata. * @@ -20715,6 +22394,41 @@ export interface PermissionsConfigureResult { */ success: boolean; } +/** + * The verbatim managed `permissions` slice to interpret. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionsEvaluateManagedPolicyParams". + */ +/** @experimental */ +/** @internal */ +export interface PermissionsEvaluateManagedPolicyParams { + /** + * The `permissions` object from a managed settings layer, for example `{"disableBypassPermissionsMode":"disable"}`. Omitted or null means no managed policy applies. + */ + permissions?: JsonValue; +} +/** + * The policy questions a host asks before granting elevated permissions. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionsEvaluateManagedPolicyResult". + */ +/** @experimental */ +export interface PermissionsEvaluateManagedPolicyResult { + /** + * True when managed policy forbids bypass-permissions mode, so allow-all style flags must not take effect. + */ + bypassPermissionsDisabled: boolean; + /** + * 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: boolean; + /** + * True when the managed layer supplies permission rules that the session must enforce. + */ + permissionRulesActive: boolean; +} /** * Indicates whether the operation succeeded. * @@ -20983,6 +22697,44 @@ export interface PermissionUrlsSetUnrestrictedModeParams { */ enabled: boolean; } +/** + * A complete JSON snapshot to write under the runtime's per-path file lock. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PersistenceWriteRawJsonLockedRequest". + */ +/** @experimental */ +export interface PersistenceWriteRawJsonLockedRequest { + /** + * Absolute path of the file to replace. Parent directories are created when missing. + */ + path: string; + /** + * The serialized JSON document to write. It replaces the file's contents entirely; nothing is merged. + */ + dataJson: string; + /** + * Comment lines to emit above the document, for a format that tolerates them. Omit for strict JSON. + */ + fileHeader?: string | null; +} +/** + * The outcome of a locked snapshot write. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PersistenceWriteRawJsonLockedResult". + */ +/** @experimental */ +export interface PersistenceWriteRawJsonLockedResult { + /** + * Whether the file now holds the requested contents. + */ + success: boolean; + /** + * 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 | null; +} /** * Optional message to echo back to the caller. * @@ -23100,6 +24852,131 @@ export interface RemoteSessionRepository { */ branch?: string; } +/** + * One atomic edit of an allowed top-level repository setting. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "RepoSettingsEditRequest". + */ +/** @experimental */ +/** @internal */ +export interface RepoSettingsEditRequest { + /** + * Absolute path of the repository root. + */ + repoRoot: string; + scope: RepoSettingsEditScope; + /** + * One canonical top-level repository setting that the allowlist permits. + */ + key: string; + 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. + */ + value?: JsonValue; +} +/** + * Which repository settings file to read. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "RepoSettingsReadRawRequest". + */ +/** @experimental */ +/** @internal */ +export interface RepoSettingsReadRawRequest { + /** + * Absolute path of the repository root. + */ + repoRoot: string; + /** + * Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + */ + scope: string; +} +/** + * Parsed repository settings without normalization or layering. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "RepoSettingsReadRawResult". + */ +/** @experimental */ +/** @internal */ +export interface RepoSettingsReadRawResult { + /** + * Stored JSON values after the existing JSONC parser and prototype-safety checks. Empty when the file is missing, blank, or invalid. + */ + config: JsonValue; + /** + * True when the file exists but did not parse, so `config` is empty for that reason rather than because nothing is set. + */ + invalid: boolean; +} +/** + * The complete set of repository-overridable settings to store. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "RepoSettingsWriteAllRequest". + */ +/** @experimental */ +/** @internal */ +export interface RepoSettingsWriteAllRequest { + /** + * Absolute path of the repository root. + */ + repoRoot: string; + /** + * Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + */ + scope: string; + /** + * Object of allowed settings to store. Missing allowlisted keys are removed. Other input keys are ignored, while unknown stored keys are preserved. + */ + settings: JsonValue; +} +/** + * One key to write or remove in a repository settings file. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "RepoSettingsWriteKeyRequest". + */ +/** @experimental */ +/** @internal */ +export interface RepoSettingsWriteKeyRequest { + /** + * Absolute path of the repository root. + */ + repoRoot: string; + /** + * Use repo for shared settings or local for settings.local.json. Other scopes are rejected. + */ + scope: string; + /** + * Canonical top-level key permitted by the repository settings allowlist. + */ + key: string; + /** + * Value to validate and write. Required unless delete is true; a null value is validated, not treated as deletion. + */ + value?: JsonValue; + /** + * True to remove the key instead of writing it. + */ + delete?: boolean; +} +/** + * The stored sandbox settings to resolve against the defaults. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxApplyDefaultsRequest". + */ +/** @experimental */ +export interface SandboxApplyDefaultsRequest { + /** + * The sandbox settings as stored in the settings file. An empty object resolves to the defaults alone. + */ + stored: JsonValue; +} /** * Resolved sandbox configuration. * @@ -23375,6 +25252,69 @@ export interface SandboxDisableForSessionResult { */ enabled: boolean; } +/** + * The documentation URL a host cites when it declines or downgrades a sandbox setting. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxDocsUrlResult". + */ +/** @experimental */ +export interface SandboxDocsUrlResult { + /** + * Documentation URL describing the sandbox and the settings that control it. + */ + url: string; +} +/** + * A sandbox config and the per-feature routing flag to resolve it for. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxEffectiveForOptionalParams". + */ +/** @experimental */ +/** @internal */ +export interface SandboxEffectiveForOptionalParams { + /** + * The session's resolved `sandbox` slice. Omitted when the host has none, which yields a disabled sandbox rather than an error. + */ + sandbox?: JsonValue; + /** + * The routing flag naming the feature being launched, such as `sandboxMcpServers` or `sandboxLspServers`. + */ + routingFlag: string; +} +/** + * The sandbox a named feature actually launches under. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxEffectiveForOptionalResult". + */ +/** @experimental */ +/** @internal */ +export interface SandboxEffectiveForOptionalResult { + /** + * The effective sandbox config for the requested feature, or a disabled sandbox when the feature is routed out of it. + */ + sandboxJson: JsonValue; +} +/** + * One slash command, as typed, to test against the enforcement-block allowlist. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxEnforcementCommandAllowedParams". + */ +/** @experimental */ +/** @internal */ +export interface SandboxEnforcementCommandAllowedParams { + /** + * The command name as the user typed it, with or without its leading slash. + */ + commandName: string; + /** + * 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[]; +} /** * Managed sandbox enforcement state for a session. * @@ -23396,6 +25336,60 @@ export interface SandboxEnforcementStatus { */ reason?: string; } +/** + * A batch of file-picker candidates to check against a prepared policy. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxFilterPickerCandidatesParams". + */ +/** @experimental */ +/** @internal */ +export interface SandboxFilterPickerCandidatesParams { + /** + * The candidates to check, in the order the answers are returned. + */ + candidates: SandboxPickerCandidate[]; + /** + * The session's working directory, matching the one the policy was prepared from. + */ + cwd: string; + /** + * The policy returned by `sandbox.preparePickerPolicy`. Omitted keeps every candidate. + */ + sandbox?: JsonValue; +} +/** + * One file-picker suggestion offered up for the sandbox filesystem check. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxPickerCandidate". + */ +/** @experimental */ +/** @internal */ +export interface SandboxPickerCandidate { + /** + * Absolute path the suggestion would attach. + */ + path: string; + /** + * 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: boolean; +} +/** + * Per-candidate allow decisions, positionally aligned with the request. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxFilterPickerCandidatesResult". + */ +/** @experimental */ +/** @internal */ +export interface SandboxFilterPickerCandidatesResult { + /** + * One decision per candidate, in the same order. Always the same length as the request's `candidates`. + */ + allowed: boolean[]; +} /** * Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. * @@ -23462,6 +25456,38 @@ export interface SandboxHostSupport { */ capabilities: SandboxHostCapability[]; } +/** + * The working directory and sandbox settings to build a file-picker policy from. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxPreparePickerPolicyParams". + */ +/** @experimental */ +/** @internal */ +export interface SandboxPreparePickerPolicyParams { + /** + * The session's working directory. Git discovery and grant canonicalization are resolved against it. + */ + cwd: string; + /** + * The session's resolved `sandbox` slice. Omitted, or disabled, yields no policy and every candidate is kept. + */ + sandbox?: JsonValue; +} +/** + * The prepared file-picker policy, or none when the sandbox is disabled. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxPreparePickerPolicyResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + sandboxJson?: JsonValue; +} /** * Result of creating the persistent certificate authority of the sandbox credential proxy. * @@ -23503,6 +25529,125 @@ export interface SandboxProxyCaStatus { */ canInstall: boolean; } +/** + * The stored sandbox proxy password, if one is present. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxProxySecretGetResult". + */ +/** @experimental */ +export interface SandboxProxySecretGetResult { + /** + * The stored password. Absent when no entry exists in either the keychain or the on-disk fallback store. + */ + value?: string; +} +/** + * Where this runtime keeps the sandbox proxy password. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxProxySecretLocation". + */ +/** @experimental */ +export interface 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. + */ + configDir: 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. + */ + secretId: string; +} +/** + * The sandbox proxy password to store. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxProxySecretSaveRequest". + */ +/** @experimental */ +export interface SandboxProxySecretSaveRequest { + /** + * The password to store for the sandbox proxy. + */ + value: string; +} +/** + * Where the sandbox proxy password was stored. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxProxySecretSaveResult". + */ +/** @experimental */ +export interface 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`. + */ + storedInKeychain: boolean; +} +/** + * The user's sandbox settings and the managed sandbox floor to raise them to. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxResolveFlooredConfigParams". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + userSandbox?: JsonValue; + /** + * 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?: JsonValue; +} +/** + * The user's sandbox settings raised to the managed floor. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxResolveFlooredConfigResult". + */ +/** @experimental */ +export interface SandboxResolveFlooredConfigResult { + /** + * The floored sandbox config, normalized through the declared sandbox shape so only declared fields in a fixed order are present. + */ + sandboxJson: JsonValue; +} +/** + * The managed sandbox policy in force, which selects which remedy the notice offers. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxUnsupportedStartupNoticeParams". + */ +/** @experimental */ +/** @internal */ +export interface SandboxUnsupportedStartupNoticeParams { + /** + * 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?: JsonValue; + /** + * 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?: boolean; +} +/** + * The startup warning, absent when the machine can run a sandbox. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxUnsupportedStartupNoticeResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + notice?: string; +} /** * Register an absolute-time scheduled prompt. * @@ -25196,6 +27341,10 @@ export interface SessionOpenOptions { */ enforceManagedModelDefaults?: boolean; managedSettings?: SessionManagedSettings; + /** + * Declares that a human drives this session's conversation, enabling user-message analysis (sentiment, task completion criteria, and task analytics) for user-authored messages. Automated messages remain excluded. Defaults to false for SDK sessions. Applied when the session is created or loaded from storage; ignored when resuming a session that is already active, and for cloud sessions. + */ + interactiveUser?: boolean; /** * Opt in to capturing file changes for session rewind and session diff. Capture cannot reconstruct changes made before it was enabled. On create it starts capture from the first turn. It is also honored on resume: for a session that already has tracked prior turns, tracking continues automatically even if this is omitted; passing it on resume additionally enables tracking for an eligible session that has no prior root turn yet. Resuming a session whose prior root turns were never tracked has no restorable baseline, so tracking stays disabled for it and rewind reports file change tracking as unavailable; the resume itself still succeeds, so sessions that predate tracking remain loadable. The opt-in is only rejected when the session can never track (a subagent session, or one without local session storage). It is intentionally absent from the mutable options update because enabling it after edits have occurred would create an incomplete, misleading baseline. Subagents share the parent session's capture store and are not tracked as separate rewind points: a file a subagent writes is attributed to whichever root user turn was open when the capture was staged, just before the tool body ran. A turn cannot open while a staged capture is still in flight, so a subagent tool that staged under the spawning turn stays attributed to it however late the write lands, while a capture it stages after the user's next message belongs to that later turn. Attribution decides which turn's rewind point counts and file preview include that write; it does not narrow which rewinds revert it, because a rewind restores every capture from the selected turn onward, so the earlier spawning turn reverts it as well. */ @@ -25319,15 +27468,27 @@ export interface SessionOpenOptions { */ allowAllMcpServerInstructions?: boolean; /** - * 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[]; + /** + * 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[]; + /** + * 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[]; + /** + * 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[]; /** * Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. */ ignoredSkillsLocations?: string[]; /** - * 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?: boolean; /** @@ -27133,9 +29294,21 @@ export interface SessionUpdateOptionsParams { */ allowAllMcpServerInstructions?: boolean; /** - * 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[]; + /** + * 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[]; + /** + * 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[]; + /** + * 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[]; /** * Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. */ @@ -27243,7 +29416,7 @@ export interface SessionUpdateOptionsParams { */ organizationCustomInstructions?: string; /** - * 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?: boolean; /** @@ -27255,7 +29428,7 @@ export interface SessionUpdateOptionsParams { */ enableSessionStore?: boolean; /** - * 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?: boolean; contextTier?: OptionsUpdateContextTier; @@ -27281,6 +29454,17 @@ export interface SessionUpdateOptionsResult { */ pluginHookCount?: number; } +/** + * Outcome of an atomic user or repository settings edit. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SettingsEditResult". + */ +/** @experimental */ +/** @internal */ +export interface SettingsEditResult { + status: SettingsEditStatus; +} /** * User-requested shell execution cancellation handle. * @@ -27589,6 +29773,21 @@ export interface SkillList { */ skills: Skill[]; } +/** + * Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SkillPlanInstallFromReferenceRequest". + */ +/** @experimental */ +export interface SkillPlanInstallFromReferenceRequest { + contract: CatalogClientContract; + reference: CatalogMaterializationReference; + /** + * Existing local session attached to this connection. + */ + policySessionId: string; +} /** * Side-effect-free planning of one verified Agent Finder Skill candidate. * @@ -27758,7 +29957,7 @@ export interface SkillsDiscoverRequest { */ projectPaths?: string[]; /** - * 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[]; /** @@ -27813,6 +30012,10 @@ export interface SkillSetEnabledRequest { */ /** @experimental */ export interface SkillsGetDiscoveryPathsRequest { + /** + * 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[]; /** * Optional list of project directory paths. When omitted or empty, only personal and custom directories are returned. */ @@ -29900,6 +32103,162 @@ export interface UserSettingMetadata { */ isDefault: boolean; } +/** + * A single top-level setting subtree to validate and write. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsApplyTopKeyRequest". + */ +/** @experimental */ +export interface UserSettingsApplyTopKeyRequest { + /** + * Canonical path of the setting the caller is editing. Currently informational only. + */ + canonicalPath: string[]; + /** + * Top-level setting name to write. + */ + topKey: string; + /** + * Value to write for the whole top-level key. Omitted when `delete` is true. Explicit null is preserved and validated as null. + */ + value?: JsonValue | null; + /** + * True to remove the top-level key instead of writing it. + */ + delete?: boolean; + /** + * 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?: boolean; +} +/** + * Outcome of validating and writing one top-level setting subtree. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsApplyTopKeyResult". + */ +/** @experimental */ +export interface 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. + */ + status: string; + /** + * Problems found, present only when `status` is `validation-error`. + */ + issues?: UserSettingsValidationIssue[]; + /** + * Why the write failed, present only when `status` is `write-error`. + */ + error?: string | null; +} +/** + * One problem found while validating a setting value. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsValidationIssue". + */ +/** @experimental */ +export interface 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. + */ + path: JsonValue[]; + /** + * Human-readable description of the problem. + */ + message: string; +} +/** + * The overrides and host context to resolve effective runtime settings from. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsBuildRuntimeRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsBuildRuntimeRequest { + /** + * 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?: { + [k: string]: JsonValue | undefined; + }; + /** + * 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; + /** + * 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: { + [k: string]: string | undefined; + }; + /** + * The platform to apply platform-specific defaults for, in the same spelling the host's own runtime reports. + */ + platform: string; +} +/** + * One atomic edit of a canonical setting in settings.json. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsEditRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsEditRequest { + /** + * Configuration directory to edit. Omitted or empty resolves the server's own home, as in `user.settings.loadForConfigDir`. + */ + configDir?: string; + /** + * Nonempty canonical path segments of the setting to edit, starting with its top-level key. + * + * @minItems 1 + */ + canonicalPath: [string, ...string[]]; + 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. + */ + value?: JsonValue; +} +/** + * Selects which settings file paths to resolve. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsGetPathsRequest". + */ +/** @experimental */ +export interface 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. + */ + repoRoot?: string; +} +/** + * On-disk locations of the settings files this runtime reads. A path is reported whether or not the file exists. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsGetPathsResult". + */ +/** @experimental */ +export interface 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. + */ + userSettings: string; + /** + * The repository's checked-in settings file, or null when no repository root was supplied. + */ + repoSettings?: string | null; + /** + * The repository's local, untracked settings file, or null when no repository root was supplied. + */ + repoLocalSettings?: string | null; +} /** * Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. * @@ -29915,6 +32274,538 @@ export interface UserSettingsGetResult { [k: string]: UserSettingMetadata; }; } +/** + * Top-level user-setting keys grouped by who may govern them. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsGovernanceKeysResult". + */ +/** @experimental */ +export interface UserSettingsGovernanceKeysResult { + /** + * Top-level keys a repository may override through `.github/copilot/settings.json` or `.github/copilot/settings.local.json`. + */ + repo: string[]; + /** + * Top-level keys a repository may override through a Claude-compatible repository config. + */ + claudeRepo: string[]; + /** + * Top-level keys an enterprise policy may fix through managed settings. + */ + managed: string[]; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsHostContext". + */ +/** @experimental */ +export interface UserSettingsHostContext { + /** + * The Copilot configuration directory, when the host set one. Takes precedence over `COPILOT_HOME` and the home directory. + */ + configDir?: string; + /** + * The user's home directory, used when neither a configuration directory nor `COPILOT_HOME` is set. + */ + homeDirectory: string; + /** + * The host's environment variables. `COPILOT_HOME` is read from here, as are the variables a settings hook may expand. + */ + environment: {}; +} +/** + * A repository to read settings from, and the user layer they are merged over. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadAndMergeRepoRequest". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + repoRoot: 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. + */ + userSettings: { + [k: string]: JsonValue | undefined; + }; +} +/** + * The settings a consumer should act on inside a repository, together with any diagnostic raised while reading the repository layer. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadAndMergeRepoResult". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsLoadAndMergeRepoResult { + /** + * The merged settings document, keyed by setting name. Equals the supplied user layer when the repository declares no settings. + */ + settings: { + [k: string]: JsonValue | undefined; + }; + /** + * 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 | null; +} +/** + * Selects the configuration directory whose user settings to read. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadForConfigDirRequest". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + configDir?: string; +} +/** + * Contents of settings.json as stored, for a caller about to rewrite it. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadForEditResult". + */ +/** @experimental */ +export interface UserSettingsLoadForEditResult { + /** + * `ok` when the file parsed, `missing` when there is no file yet, `invalid` when the file exists but will not parse. + */ + status: string; + /** + * Parsed contents, present only when `status` is `ok`. + */ + settings?: JsonValue; +} +/** + * Selects the repository whose checked-in settings to read. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadRepoRequest". + */ +/** @experimental */ +export interface 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. + */ + repoRoot: string; +} +/** + * A repository's settings as stored, together with any diagnostic raised while reading them. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadRepoResult". + */ +/** @experimental */ +export interface UserSettingsLoadRepoResult { + /** + * The repository's settings document, keyed by setting name, with each value carried verbatim. Null when the repository declares no settings. + */ + settings?: { + [k: string]: JsonValue | undefined; + } | null; + /** + * 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 | null; +} +/** + * The user's stored settings, together with any diagnostic raised while reading them. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsLoadWithWarningResult". + */ +/** @experimental */ +export interface 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. + */ + settings: { + [k: string]: JsonValue | undefined; + }; + /** + * 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 | null; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMergeManagedAndUserParams". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + managed?: JsonValue; + /** + * 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?: JsonValue; +} +/** + * The user layer folded over the managed layer. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMergeManagedAndUserResult". + */ +/** @experimental */ +export interface UserSettingsMergeManagedAndUserResult { + /** + * The merged settings. An empty object when neither layer was supplied, so an unmanaged host with no user settings governs nothing. + */ + settingsJson: JsonValue; +} +/** + * The user layer and the repository layer to fold together. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMergeRepoValueParams". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsMergeRepoValueParams { + /** + * The user's settings, already resolved against any managed layer. + */ + userSettings: JsonValue; + /** + * The repository's checked-in settings layer. + */ + repoSettings: JsonValue; +} +/** + * The repository layer folded into the user layer. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMergeRepoValueResult". + */ +/** @experimental */ +export interface UserSettingsMergeRepoValueResult { + /** + * The merged settings, with the per-entry managed control maps re-stamped from the runtime's ambient managed policy. + */ + settingsJson: JsonValue; +} +/** + * Selects the configuration directory whose settings document to clean. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMigrateObsoleteOnDiskRequest". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + configDir?: string; +} +/** + * The outcome of the one-time obsolete-settings cleanup. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMigrateObsoleteOnDiskResult". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsMigrateObsoleteOnDiskResult { + 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMigrateStaffLogLevelOnceRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsMigrateStaffLogLevelOnceRequest { + context: UserSettingsHostContext; + /** + * 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; +} +/** + * The outcome of the one-time staff log-level migration. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMigrateStaffLogLevelOnceResult". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsMigrateStaffLogLevelOnceResult { + status: StaffLogLevelMigrationStatus; + /** + * When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. + */ + completedAt?: string; + /** + * 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?: {}; +} +/** + * The outcome of the one-time staff update-channel migration. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsMigrateStaffUpdateChannelOnceResult". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsMigrateStaffUpdateChannelOnceResult { + status: StaffUpdateChannelMigrationStatus; + /** + * When the migration was recorded as done, as an ISO-8601 timestamp. Absent only for `ineligible`, which records nothing. + */ + completedAt?: string; +} +/** + * Settings object whose key names should be canonicalized. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsNormalizeRequest". + */ +/** @experimental */ +export interface UserSettingsNormalizeRequest { + /** + * Free-form settings object, possibly using legacy or snake_case key names. + */ + settings: JsonValue; +} +/** + * Settings object with canonical key names. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsNormalizeResult". + */ +/** @experimental */ +export interface UserSettingsNormalizeResult { + /** + * The same settings with every legacy or snake_case key renamed to its canonical spelling. + */ + settings: JsonValue; +} +/** + * The on-disk location of the user's settings document. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsPathResult". + */ +/** @experimental */ +export interface 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. + */ + path: string; +} +/** + * One setting that will not take effect, and why. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsProblem". + */ +/** @experimental */ +export interface UserSettingsProblem { + /** + * Stable identifier for this problem within the reported set, so a caller can track a selection across refreshes. + */ + id: string; + /** + * What is wrong in machine-readable form, for a caller that wants to branch rather than display. + */ + kind: string; + /** + * Coarse grouping for the problem, for a caller that presents problems in sections. + */ + category: string; + source: UserSettingsProblemLocation; + /** + * 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; + /** + * Why the setting will not take effect. Never contains the setting's value, which may be a token or another secret. + */ + explanation: string; +} +/** + * The settings layer a problem was found in. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsProblemLocation". + */ +/** @experimental */ +export interface UserSettingsProblemLocation { + /** + * Which layer this is: `user`, `repo`, or `local`. + */ + kind: string; + /** + * The caller-supplied display label for the layer. + */ + label: string; + /** + * Absolute path of the file backing this layer. + */ + filePath: string; +} +/** + * One already-loaded settings layer to diagnose. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsProblemSource". + */ +/** @experimental */ +export interface UserSettingsProblemSource { + /** + * Which layer this is: `user`, `repo`, or `local`. + */ + 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. + */ + label: string; + /** + * Absolute path of the file backing this layer. + */ + filePath: string; + /** + * The layer's settings exactly as loaded, unnormalized. Empty when the file is missing or failed to parse. + */ + config?: { + [k: string]: unknown | undefined; + }; + /** + * True when the file itself failed to parse, so its keys could not be read at all. + */ + invalid?: boolean; +} +/** + * The settings layers to diagnose. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsProblemsRequest". + */ +/** @experimental */ +export interface UserSettingsProblemsRequest { + /** + * Layers to diagnose, in the precedence order the caller applies them. + */ + sources?: UserSettingsProblemSource[]; +} +/** + * The settings that will not take effect across the given layers. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsProblemsResult". + */ +/** @experimental */ +export interface UserSettingsProblemsResult { + /** + * Each setting that will not take effect, and why. Empty when every layer is healthy. + */ + problems?: UserSettingsProblem[]; +} +/** + * 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. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsResetStaffModelSelectionOnceRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsResetStaffModelSelectionOnceRequest { + 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. + */ + staff?: boolean; + /** + * 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; +} +/** + * The outcome of the one-time staff model-selection reset. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsResetStaffModelSelectionOnceResult". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsResetStaffModelSelectionOnceResult { + /** + * Whether the persisted model selection was cleared. Only `true` means the settings document changed and the host should adopt the returned settings. + */ + didReset: boolean; + /** + * 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; + /** + * 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; + /** + * The settings document as it stands after the reset, keyed by setting name. Present only when `didReset` is true. + */ + settings?: {}; +} +/** + * One hook to disable or re-enable. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsSetHookDisabledRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsSetHookDisabledRequest { + /** + * Path of the settings.json to rewrite. + */ + settingsPath: string; + /** + * Durable content-hash key of the hook, as `hooks.loadResources` reports it. + */ + disableKey: string; + /** + * True adds the key to `disabledHooks`, false removes it. + */ + disabled: boolean; +} +/** + * Outcome of rewriting the `disabledHooks` setting. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsSetHookDisabledResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + status: string; + /** + * Why the write failed. Absent when the status is `ok`. + */ + errorMessage?: 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. * @@ -29928,6 +32819,122 @@ export interface UserSettingsSetRequest { */ settings: JsonValue; } +/** + * Selects the configuration directory whose user settings document should be inspected for unrecognized top-level keys. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsUnknownKeysWarningRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsUnknownKeysWarningRequest { + context: UserSettingsHostContext; +} +/** + * The unrecognized top-level keys found in the user settings document, already phrased for display. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsUnknownKeysWarningResult". + */ +/** @experimental */ +/** @internal */ +export interface 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. + */ + warning?: string; +} +/** + * Names the user setting to merge into, and the partial object to merge. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsUpdateRequest". + */ +/** @experimental */ +/** @internal */ +export interface UserSettingsUpdateRequest { + /** + * Top-level user-setting key to merge into, such as `extensions`. + */ + key: string; + /** + * 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: JsonValue; + /** + * 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; + /** + * 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; +} +/** + * 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. * @@ -31558,6 +34565,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. * @@ -31680,37 +34783,6 @@ export interface HostGetConfigurationResult { */ /** @experimental */ export interface HostReadyResult {} -/** - * 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[]; -} /** @experimental */ export interface SessionWorkflowPauseAtCheckpointResult { @@ -32238,6 +35310,159 @@ export interface InstallationsConfirmRequest { /** Create typed server-scoped RPC methods (no session required). */ export function createServerRpc(connection: MessageConnection) { return { + /** @experimental */ + sandbox: { + /** @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. + * + * @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 Status of the persistent certificate authority of the sandbox credential proxy. + */ + getStatus: async (params: SandboxProxyCaRequest): Promise => + connection.sendRequest("sandbox.proxyCa.getStatus", params), + /** + * 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. + * + * @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 Result of creating the persistent certificate authority of the sandbox credential proxy. + */ + create: async (params: SandboxProxyCaRequest): Promise => + connection.sendRequest("sandbox.proxyCa.create", params), + /** + * 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. + * + * @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 Status of the persistent certificate authority of the sandbox credential proxy. + */ + rotate: async (params: SandboxProxyCaRequest): Promise => + connection.sendRequest("sandbox.proxyCa.rotate", params), + /** + * 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. + * + * @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 Status of the persistent certificate authority of the sandbox credential proxy. + */ + trust: async (params: SandboxProxyCaRequest): Promise => + connection.sendRequest("sandbox.proxyCa.trust", params), + /** + * 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. + * + * @returns Status of the persistent certificate authority of the sandbox credential proxy. + */ + 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: { /** @@ -32350,60 +35575,16 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("models.getBuiltInCatalog", {}), }, /** @experimental */ - sandbox: { + contentExclusion: { /** - * Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. + * Validates candidate content-exclusion policies and returns them normalized, without starting a session. This runs the same parser the runtime's own policy enforcement uses, so a host cannot accept a policy set the runtime would later reject, nor reject one it would have honored. Call it when policies arrive from outside the runtime — a command-line flag, a configuration file, a host's own settings — and must be checked before a session exists to apply them to. A malformed document fails with a message prefixed `Invalid JSON: `; a well-formed document that violates the policy schema fails with the offending field paths. * - * @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. + * @param params Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser. + * + * @returns The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this result. */ - getHostSupport: async (): Promise => - connection.sendRequest("sandbox.getHostSupport", {}), - /** @experimental */ - proxyCa: { - /** - * 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. - * - * @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 Status of the persistent certificate authority of the sandbox credential proxy. - */ - getStatus: async (params: SandboxProxyCaRequest): Promise => - connection.sendRequest("sandbox.proxyCa.getStatus", params), - /** - * 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. - * - * @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 Result of creating the persistent certificate authority of the sandbox credential proxy. - */ - create: async (params: SandboxProxyCaRequest): Promise => - connection.sendRequest("sandbox.proxyCa.create", params), - /** - * 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. - * - * @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 Status of the persistent certificate authority of the sandbox credential proxy. - */ - rotate: async (params: SandboxProxyCaRequest): Promise => - connection.sendRequest("sandbox.proxyCa.rotate", params), - /** - * 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. - * - * @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 Status of the persistent certificate authority of the sandbox credential proxy. - */ - trust: async (params: SandboxProxyCaRequest): Promise => - connection.sendRequest("sandbox.proxyCa.trust", params), - /** - * 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. - * - * @returns Status of the persistent certificate authority of the sandbox credential proxy. - */ - remove: async (): Promise => - connection.sendRequest("sandbox.proxyCa.remove", {}), - }, + validatePolicies: async (params: ContentExclusionValidatePoliciesRequest): Promise => + connection.sendRequest("contentExclusion.validatePolicies", params), }, /** @experimental */ tools: { @@ -32644,6 +35825,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. @@ -32654,6 +35856,15 @@ export function createServerRpc(connection: MessageConnection) { */ planInstall: async (params: SkillPlanInstallRequest): Promise => connection.sendRequest("skills.planInstall", params), + /** + * Plans installation of the Skill pinned by an Agent Finder materialisation reference without writing files. The runtime retrieves the descriptor only from its fixed Agent Finder authority and applies the same verification as skills.planInstall; the review shows only verified data. Apply the returned plan with skills.applyInstall. + * + * @param params Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate. + * + * @returns Skill installation management result with the honoured contract, or a typed refusal. + */ + planInstallFromReference: async (params: SkillPlanInstallFromReferenceRequest): Promise => + connection.sendRequest("skills.planInstallFromReference", params), /** * Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled. * @@ -32927,27 +36138,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. @@ -32958,87 +36148,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), - /** @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 */ runtime: { /** * Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. @@ -33367,6 +36476,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: { /** @@ -33423,6 +37080,43 @@ 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: { @@ -33452,6 +37146,13 @@ export function createInternalServerRpc(connection: MessageConnection) { 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. * @@ -33463,6 +37164,30 @@ export function createInternalServerRpc(connection: MessageConnection) { 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. @@ -34145,6 +37870,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. * diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 3b15ae7d4f..b799ac55f5 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 */ @@ -2187,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; /** @@ -2198,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 @@ -4257,12 +4267,6 @@ export interface CompactionCompleteEvent { * Conversation compaction results including success status, metrics, and optional error details */ export interface CompactionCompleteData { - /** - * Legacy active-workflow reminder retained for replay compatibility - * - * @internal - */ - activeFactorySummary?: string; /** * Authoritative active-workflow reminder appended to the compacted context * @@ -4389,6 +4393,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 @@ -4537,6 +4545,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; } @@ -8735,10 +8751,6 @@ export interface SubagentStartedData { * Whether the sub-agent runs synchronously or in the background. */ executionMode?: string; - /** - * Legacy root id of the workflow run that spawned this sub-agent. New consumers should use workflowRunId. - */ - factoryRunId?: string; /** * Model the sub-agent will run with, when known at start. */ @@ -9672,6 +9684,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; diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index ad9e427287..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, diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 02e60dfbcf..6dd16d357b 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -183,6 +183,27 @@ function deserializeHookInput(raw: unknown): unknown { }; } +/** + * The CLI sends the `error` of an errorOccurred or sessionEnd hook input as an + * error object (`{ name, message, stack }` or `{ message }`), where the hook + * input types declare a string. The object's message becomes the error, and an + * object without a string message becomes its JSON. Other values pass through. + */ +function normalizeHookError(hookType: string, input: unknown): unknown { + if (hookType !== "errorOccurred" && hookType !== "sessionEnd") { + return input; + } + if (!input || typeof input !== "object" || !("error" in input)) { + return input; + } + const { error } = input as { error: unknown }; + if (!error || typeof error !== "object" || Array.isArray(error)) { + return input; + } + const { message } = error as { message?: unknown }; + return { ...input, error: typeof message === "string" ? message : JSON.stringify(error) }; +} + function isOpenCanvasInstance(value: unknown): value is OpenCanvasInstance { if (!value || typeof value !== "object") { return false; @@ -2364,7 +2385,7 @@ export class CopilotSession { // is left alone — the user-facing handler types still cast unknown to the // specific HookInput, so a runtime type mismatch surfaces as a normal // TypeError in user code rather than being silently masked. - const normalized = deserializeHookInput(input); + const normalized = normalizeHookError(hookType, deserializeHookInput(input)); type GenericHandler = ( input: unknown, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 80588c2627..1d8fdb3247 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1519,6 +1519,12 @@ export type AutoModeSwitchHandler = ( * Base interface for all hook inputs */ export interface BaseHookInput { + /** Subagent actor ID, absent for root hooks and older runtimes. */ + agentId?: string; + /** Subagent type, when known by the runtime. */ + agentType?: string; + /** Immediate parent actor ID, when available. */ + parentAgentId?: string; /** The runtime session ID associated with the hook. Child tool hooks use * the child session ID; sub-agent lifecycle hooks use the parent session ID, * matching `invocation.sessionId`. */ @@ -2025,6 +2031,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. */ @@ -2960,7 +2968,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[]; @@ -2981,6 +2993,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. @@ -2997,7 +3030,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[]; @@ -3062,6 +3100,14 @@ export interface SessionConfigBase { */ enforceManagedModelDefaults?: boolean; + /** + * Declares that a human drives this session's conversation, enabling + * user-message analysis telemetry for user-authored messages. Automated + * messages remain excluded. Defaults to false for SDK sessions; ignored + * for cloud sessions and when resuming a session that is already active. + */ + interactiveUser?: boolean; + /** * Host-injected enterprise managed settings for this session. * @@ -3115,9 +3161,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; @@ -3135,6 +3181,14 @@ export interface SessionConfigBase { */ enableSessionStore?: boolean; + /** + * When true, requests the read-only GitHub cache store for this session + * (the `github-cache-store` capability). The runtime registers the + * `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is + * also on. Defaults to disabled. + */ + enableGitHubCacheStore?: boolean; + /** * When true, enables skill loading (including builtin skills and discovered * skill directories). When false, no skills are loaded regardless of @@ -3197,6 +3251,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/client.test.ts b/nodejs/test/client.test.ts index 22831ad930..0eb703015b 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -4895,6 +4895,105 @@ connection.listen(); }); expect(topLevelStop).not.toHaveBeenCalled(); }); + + it.each([ + [ + "an error object", + { name: "Error", message: "model timeout", stack: "Error: model timeout" }, + "model timeout", + ], + ["a string", "boom", "boom"], + ["null", null, null], + ["an object without a string message", { code: 7 }, '{"code":7}'], + ])("decodes %s in the error hook inputs", async (_name, error, expected) => { + const client = new CopilotClient({ autoStart: false }); + const received: { hookType: string; error: unknown }[] = []; + const session = new CopilotSession("session-1", {} as any); + session.registerHooks({ + onErrorOccurred: (input) => { + received.push({ hookType: "errorOccurred", error: input.error }); + }, + onSessionEnd: (input) => { + received.push({ hookType: "sessionEnd", error: input.error }); + }, + }); + (client as any).sessions.set(session.sessionId, session); + const base = { sessionId: session.sessionId, timestamp: 1700000000000, cwd: "/repo" }; + + await (client as any).handleHooksInvoke({ + sessionId: session.sessionId, + hookType: "errorOccurred", + input: { ...base, error, errorContext: "model_call", recoverable: true }, + }); + await (client as any).handleHooksInvoke({ + sessionId: session.sessionId, + hookType: "sessionEnd", + input: { ...base, reason: "error", error }, + }); + + expect(received).toEqual([ + { hookType: "errorOccurred", error: expected }, + { hookType: "sessionEnd", error: expected }, + ]); + }); + + it("keeps the decoded timestamp and working directory when it decodes the error", async () => { + const client = new CopilotClient({ autoStart: false }); + const received: { timestamp: unknown; workingDirectory: unknown }[] = []; + const session = new CopilotSession("session-1", {} as any); + session.registerHooks({ + onErrorOccurred: (input) => { + received.push({ + timestamp: input.timestamp, + workingDirectory: input.workingDirectory, + }); + }, + }); + (client as any).sessions.set(session.sessionId, session); + + await (client as any).handleHooksInvoke({ + sessionId: session.sessionId, + hookType: "errorOccurred", + input: { + sessionId: session.sessionId, + timestamp: 1700000000000, + cwd: "/repo", + error: { message: "model timeout" }, + errorContext: "model_call", + recoverable: true, + }, + }); + + expect(received).toEqual([ + { timestamp: new Date(1700000000000), workingDirectory: "/repo" }, + ]); + }); + + it("leaves an absent sessionEnd error absent", async () => { + const client = new CopilotClient({ autoStart: false }); + const received: unknown[] = []; + const session = new CopilotSession("session-1", {} as any); + session.registerHooks({ + onSessionEnd: (input) => { + received.push(input); + }, + }); + (client as any).sessions.set(session.sessionId, session); + + await (client as any).handleHooksInvoke({ + sessionId: session.sessionId, + hookType: "sessionEnd", + input: { + sessionId: session.sessionId, + timestamp: 1700000000000, + cwd: "/repo", + reason: "complete", + }, + }); + + expect(received).toHaveLength(1); + expect(received[0]).not.toHaveProperty("error"); + }); }); describe("shutdown", () => { @@ -5458,6 +5557,44 @@ describe("managedSettings serialization", () => { }); }); +describe("interactiveUser serialization", () => { + async function captureParams( + method: "session.create" | "session.resume", + config: Record + ): Promise { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (requested: string, params: any) => { + if (requested === method) return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${requested}`); + }); + if (method === "session.create") { + await client.createSession({ onPermissionRequest: approveAll, ...config }); + } else { + await client.resumeSession("session-1", { onPermissionRequest: approveAll, ...config }); + } + const call = spy.mock.calls.find(([requested]) => requested === method); + return call![1]; + } + + for (const method of ["session.create", "session.resume"] as const) { + for (const interactiveUser of [true, false]) { + it(`forwards interactiveUser: ${interactiveUser} on ${method}`, async () => { + const params = await captureParams(method, { interactiveUser }); + expect(params.interactiveUser).toBe(interactiveUser); + }); + } + + it(`omits interactiveUser from ${method} when unset`, async () => { + const params = await captureParams(method, {}); + expect(params.interactiveUser).toBeUndefined(); + }); + } +}); + describe("connect handshake clientInfo", () => { // Drives verifyProtocolVersion() against a stubbed connection so we can // observe the `connect` params without spawning a runtime. `connect` maps to 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/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 5577d03761..37d6346089 100644 --- a/nodejs/test/e2e/assisted_autopilot_permission.e2e.test.ts +++ b/nodejs/test/e2e/assisted_autopilot_permission.e2e.test.ts @@ -8,7 +8,7 @@ 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, @@ -45,11 +45,38 @@ function contractFixtureName(scope: ContractScope, recommendation: ContractRecom describe("Assisted permission handling in Autopilot", async () => { const { copilotClient: client, workDir } = await createSdkTestContext({ useStdio: true }); - it("resolves Assisted recommendations before Autopilot permission recovery", 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"); @@ -443,17 +470,22 @@ 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"; @@ -484,6 +516,7 @@ describe("Assisted permission handling in Autopilot", async () => { ASSISTED_PERMISSIONS_V2: true, }, onPermissionRequest: async (request: PermissionRequest) => { + progress.permissionCallbacks++; permissionCallbacks++; if (!request.toolCallId) { throw new Error( @@ -530,19 +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); } session.on((event) => { + recordEvent(event.type); if (event.type === "permission.requested") { const promptRequest = ( event.data as { @@ -584,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" @@ -592,11 +634,13 @@ 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 }); } + progress.phase = "assert-results"; const expectedCompletions = scenario.route === "path" && scenario.lifecycle === "create" ? 2 : 1; expect(permissionCallbacks, JSON.stringify(scenario)).toBe(expectedCompletions); @@ -646,138 +690,166 @@ describe("Assisted permission handling in Autopilot", async () => { } } finally { if (session) { + progress.phase = "abort-session"; await session.abort(); + progress.phase = "disconnect-session"; await session.disconnect(); } } } - expect(providerFailures).toEqual([]); - expect(judgeOutputs).toEqual([ - ...Array(4).fill(JUDGE_OUTPUT), - ...Array(4).fill(HUMAN_REVIEW_OUTPUT), - ]); - expect(agentCalls).toBe(scenarios.length * 2 + 4); + 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; + } - 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; - 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) => { - 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", - } - ); - }, + 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; }); - await authorizationSession.sendAndWait({ - prompt: `${EARLIER_RESTRICTION_MARKER}: Do not create the authorization fixture. Reply restriction-recorded without tools.`, - }); - await configureAssistedAutopilot(authorizationSession, workDir); - authorizationSession.on((event) => { - if (event.type === "permission.requested") { - const recommendation = ( - event.data as { - promptRequest?: { - assistedApproval?: { recommendation?: string }; - }; + 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 + )}` + ); } - ).promptRequest?.assistedApproval?.recommendation; - if (recommendation) { - authorizationRecommendations.push(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); } - 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"); - await authorizationSession.send({ - prompt: `${LATER_AUTHORIZATION_MARKER}: The earlier restriction is superseded. Create the authorization fixture now and report authorization-updated.`, - }); - await taskComplete; + 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; - 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({ + 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, - summary: "authorization-updated", - }), - ]); - expect(existsSync(join(workDir, AUTHORIZATION_FIXTURE))).toBe(true); - } finally { - if (authorizationSession) { - await authorizationSession.abort(); - await authorizationSession.disconnect(); + }); + 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 = [ @@ -794,6 +866,8 @@ describe("Assisted permission handling in Autopilot", async () => { }[]; 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[] = []; @@ -810,6 +884,7 @@ describe("Assisted permission handling in Autopilot", async () => { }> = []; const taskOutcomes: Array<{ success?: boolean; summary?: string }> = []; const onPermissionRequest = () => { + progress.permissionCallbacks++; permissionCallbacks++; sequence.push("host"); if (scenario.recommendation === "approve") { @@ -866,8 +941,10 @@ describe("Assisted permission handling in Autopilot", async () => { 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 { @@ -914,12 +991,15 @@ describe("Assisted permission handling in Autopilot", async () => { : `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([]); @@ -1002,6 +1082,7 @@ describe("Assisted permission handling in Autopilot", async () => { ), 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) @@ -1018,12 +1099,15 @@ describe("Assisted permission handling in Autopilot", async () => { } } 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.slice(contractJudgeStart)).toEqual([ JUDGE_OUTPUT, @@ -1032,6 +1116,7 @@ describe("Assisted permission handling in Autopilot", async () => { HUMAN_REVIEW_OUTPUT, HUMAN_REVIEW_OUTPUT, ]); + progress.phase = "complete"; }); }); 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 fa9cc328fe..ad395c0bb9 100644 --- a/nodejs/test/e2e/compaction.e2e.test.ts +++ b/nodejs/test/e2e/compaction.e2e.test.ts @@ -15,6 +15,7 @@ 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; @@ -22,6 +23,7 @@ interface CacheWireBlock { text?: string; cache_control?: unknown; copilot_cache_control?: unknown; + output_config?: { effort?: string }; } interface CacheWireRequest extends CacheWireBlock { @@ -229,15 +231,15 @@ function systemCacheMarkers(request: CacheWireRequest): unknown[] { } for (const oneHour of [false, true]) { - describe(`Disposable Anthropic prompt caching (one-hour ${oneHour})`, async () => { - const { createClient } = await createSdkTestContext({ - replayOnly: true, - logLevel: "debug", - }); + describe.each(["CAPI Messages", "CAPI Chat", "BYOK Messages"] as const)( + `Disposable Anthropic prompt caching (one-hour ${oneHour}, %s)`, + async (route) => { + 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) => { + it(`keeps reusable prefixes but not disposable writes on ${route}`, async () => { const handler = new RefusalBudgetReplayHandler( route === "CAPI Chat" ? "/chat/completions" : "/v1/messages" ); @@ -410,16 +412,16 @@ for (const oneHour of [false, true]) { 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) => { +describe.each(["CAPI Messages", "CAPI Chat", "BYOK Messages"] as const)( + "Disposable Anthropic prompt caching during automatic compaction (%s)", + async (route) => { + const { createClient } = await createSdkTestContext({ replayOnly: true }); + it(`avoids discarded transcript writes during automatic compaction on ${route}`, async () => { const handler = new RefusalBudgetReplayHandler( route === "CAPI Chat" ? "/chat/completions" : "/v1/messages" ); @@ -501,9 +503,9 @@ describe("Disposable Anthropic prompt caching during automatic compaction", asyn } finally { await cacheClient.stop(); } - } - ); -}); + }); + } +); describe("Disposable Anthropic prompt caching during refusal fallback", async () => { const { createClient } = await createSdkTestContext({ @@ -661,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(); @@ -1387,6 +1683,10 @@ describe("Compaction", async () => { await session.disconnect(); } }); +}); + +describe("selected-tier model catalog", async () => { + const { createClient } = await createSdkTestContext(); it("should resolve selected-tier defaults and confirm a smaller-tier switch", async () => { const modelId = "gpt-5.4-mini"; class TierBudgetReplayHandler extends BudgetReplayHandler { diff --git a/nodejs/test/e2e/content_exclusion.e2e.test.ts b/nodejs/test/e2e/content_exclusion.e2e.test.ts new file mode 100644 index 0000000000..aea5fc31d4 --- /dev/null +++ b/nodejs/test/e2e/content_exclusion.e2e.test.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { describe, expect, it } from "vitest"; +import { validateContentExclusionPolicies } from "../../examples/content-exclusion.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +describe("Content-exclusion policy validation", async () => { + const { copilotClient: client } = await createSdkTestContext(); + + it("runs the policy example without a session and preserves validation errors", async () => { + await client.start(); + expect(await client.listSessions()).toEqual([]); + expect(await validateContentExclusionPolicies(client)).toEqual({ + policies: [ + { + rules: [ + { + paths: ["secrets/**"], + source: { name: "Example", type: "Organization" }, + futureRuleField: { enabled: true }, + }, + ], + last_updated_at: "2026-10-05T12:00:00Z", + scope: "all", + futurePolicyField: { nullable: null }, + }, + ], + }); + await expect( + client.rpc.contentExclusion.validatePolicies({ policiesJson: "[{" }) + ).rejects.toMatchObject({ + code: -32602, + message: expect.stringContaining("Invalid JSON: "), + }); + await expect( + client.rpc.contentExclusion.validatePolicies({ + policiesJson: '[{"rules":[],"last_updated_at":true,"scope":"invalid"}]', + }) + ).rejects.toMatchObject({ + code: -32602, + message: expect.stringMatching(/0\.last_updated_at[\s\S]*0\.scope/), + }); + expect(await client.listSessions()).toEqual([]); + }); +}); 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 38da660fb7..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: "", @@ -428,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/fixtures/hook-identity-recorder.cjs b/nodejs/test/e2e/fixtures/hook-identity-recorder.cjs new file mode 100755 index 0000000000..b9330f80e9 --- /dev/null +++ b/nodejs/test/e2e/fixtures/hook-identity-recorder.cjs @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +const { randomUUID } = require("node:crypto"); +const { writeFileSync } = require("node:fs"); +const { join } = require("node:path"); + +let body = ""; +process.stdin.setEncoding("utf8"); +process.stdin.on("data", (chunk) => { + body += chunk; +}); +process.stdin.on("end", () => { + const hook = process.argv[2]; + writeFileSync( + join(process.argv[3], `${hook}.${randomUUID()}.json`), + JSON.stringify({ hook, input: JSON.parse(body) }) + ); +}); 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 633fd4aa0a..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"; @@ -277,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"); } 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_generation.e2e.test.ts b/nodejs/test/e2e/image_generation.e2e.test.ts index 2473d39127..c365fcc255 100644 --- a/nodejs/test/e2e/image_generation.e2e.test.ts +++ b/nodejs/test/e2e/image_generation.e2e.test.ts @@ -96,7 +96,7 @@ async function imageToolOffered(session: CopilotSession): Promise { describe("Native image generation consent", async () => { // Native image inference is CAPI-only, independent of the chat-provider CI matrix. - const { createClient, openAiEndpoint } = await createSdkTestContext({ + const { createClient } = await createSdkTestContext({ replayOnly: true, backend: "capi", }); @@ -146,18 +146,6 @@ describe("Native image generation consent", async () => { ]); }); - 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({ @@ -229,6 +217,25 @@ describe("Native image generation consent", async () => { }); expect(await imageToolOffered(optedIn)).toBe(true); }); +}); + +describe("Native image generation without a CAPI image model", async () => { + const { createClient, openAiEndpoint } = await createSdkTestContext({ + replayOnly: true, + backend: "capi", + }); + + 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.each(["anthropic-messages", "openai-responses", "openai-completions"] as const)( "does not expose image generation for %s without CAPI image-model access", 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_auth_entitlement.e2e.test.ts b/nodejs/test/e2e/mcp_auth_entitlement.e2e.test.ts new file mode 100644 index 0000000000..38f23738ec --- /dev/null +++ b/nodejs/test/e2e/mcp_auth_entitlement.e2e.test.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { randomUUID } from "node:crypto"; +import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, it } from "vitest"; +import { approveAll } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +const testServer = resolve( + dirname(fileURLToPath(import.meta.url)), + "../../../test/harness/test-mcp-server.mjs" +); + +describe("MCP account entitlement changes", async () => { + const { copilotClient: client, workDir } = await createSdkTestContext(); + + it("restores policy-filtered servers and revokes them when static credentials change", async ({ + expect, + }) => { + const serverName = "account-entitlement-probe"; + const session = await client.createSession({ + onPermissionRequest: approveAll, + requestExtensions: false, + mcpServers: { + [serverName]: { + type: "local", + command: process.execPath, + args: [testServer], + workingDirectory: dirname(testServer), + tools: ["*"], + }, + }, + }); + try { + for (const enabled of [false, true, false, true]) { + const updated = await session.rpc.gitHubAuth.setCredentials({ + credentials: { + type: "token", + host: "https://github.com", + token: "synthetic-mcp-entitlement-token", + copilotUser: { is_mcp_enabled: enabled }, + }, + }); + expect(updated).toMatchObject({ success: true, copilotUserResolved: true }); + const snapshot = await session.rpc.mcp.list(); + expect(snapshot.host?.mcp3pEnabled).toBe(enabled); + expect(snapshot.host?.filteredServers.includes(serverName)).toBe(!enabled); + expect(await session.rpc.mcp.isServerRunning({ serverName })).toEqual({ + running: enabled, + }); + if (enabled) { + expect( + (await session.rpc.mcp.listTools({ serverName })).tools.map( + (tool) => tool.name + ) + ).toEqual(["get_env"]); + } + } + } finally { + await session.disconnect(); + } + }); + + it.for(["connected", "failed"] as const)( + "starts independent restored servers concurrently before reporting auth success with a %s peer", + { timeout: 120_000 }, + async (peerStatus, { expect, onTestFinished }) => { + const fixtureId = randomUUID(); + const servers = [0, 1, 2].map((index) => ({ + name: `parallel-entitlement-${index}`, + marker: join(workDir, `${fixtureId}-${index}.marker`), + gate: join(workDir, `${fixtureId}-${index}.gate`), + })); + const session = await client.createSession({ + onPermissionRequest: approveAll, + requestExtensions: false, + mcpServers: Object.fromEntries( + servers.map((server, index) => [ + server.name, + { + type: "local", + command: process.execPath, + args: [ + testServer, + "--startup-marker", + server.marker, + "--startup-gate", + server.gate, + ...(index === 1 && peerStatus === "failed" + ? ["--fail-startup"] + : []), + ], + workingDirectory: dirname(testServer), + tools: ["*"], + }, + ]) + ), + }); + const setCredentials = (enabled: boolean) => + session.rpc.gitHubAuth.setCredentials({ + credentials: { + type: "token", + host: "https://github.com", + token: "synthetic-parallel-mcp-entitlement-token", + copilotUser: { is_mcp_enabled: enabled }, + }, + }); + let restoring: ReturnType | undefined; + onTestFinished(async () => { + for (const server of servers) { + writeFileSync(server.gate, "release"); + } + if (restoring) { + await Promise.allSettled([restoring]); + } + await session.disconnect(); + }); + + // Cover deferred configs first, then configs removed from live servers. + for (const starts of [1, 2]) { + expect(await setCredentials(false)).toMatchObject({ success: true }); + const denied = await session.rpc.mcp.list(); + for (const server of servers) { + expect(denied.host?.filteredServers).toContain(server.name); + expect( + await session.rpc.mcp.isServerRunning({ serverName: server.name }) + ).toEqual({ + running: false, + }); + rmSync(server.gate, { force: true }); + } + + let restorationCompleted = false; + restoring = setCredentials(true).then((result) => { + restorationCompleted = true; + return result; + }); + await expect + .poll( + () => + servers.map((server) => + existsSync(server.marker) + ? readFileSync(server.marker, "utf8") + .split("\n") + .filter(Boolean).length + : 0 + ), + { timeout: 30_000 } + ) + .toEqual([starts, starts, starts]); + expect(restorationCompleted).toBe(false); + for (const server of servers) { + writeFileSync(server.gate, "release"); + } + expect(await restoring).toMatchObject({ success: true, copilotUserResolved: true }); + + const restored = await session.rpc.mcp.list(); + expect(restored.host?.mcp3pEnabled).toBe(true); + for (const [index, server] of servers.entries()) { + const connected = index !== 1 || peerStatus === "connected"; + expect(restored.host?.filteredServers).not.toContain(server.name); + expect( + await session.rpc.mcp.isServerRunning({ serverName: server.name }) + ).toEqual({ + running: connected, + }); + if (connected) { + expect( + ( + await session.rpc.mcp.listTools({ serverName: server.name }) + ).tools.map((tool) => tool.name) + ).toEqual(["get_env"]); + } else { + expect( + restored.servers.find((entry) => entry.name === server.name)?.status + ).toBe("failed"); + } + } + } + expect(await setCredentials(false)).toMatchObject({ success: true }); + for (const server of servers) { + expect(await session.rpc.mcp.isServerRunning({ serverName: server.name })).toEqual({ + running: false, + }); + } + } + ); +}); 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 d872e2c1c2..d067c2bcf2 100644 --- a/nodejs/test/e2e/mcp_scoped_sign_in.e2e.test.ts +++ b/nodejs/test/e2e/mcp_scoped_sign_in.e2e.test.ts @@ -126,6 +126,20 @@ describe("MCP sign-in for one server", async () => { 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, diff --git a/nodejs/test/e2e/objective_lifecycle.e2e.test.ts b/nodejs/test/e2e/objective_lifecycle.e2e.test.ts new file mode 100644 index 0000000000..7a87209d24 --- /dev/null +++ b/nodejs/test/e2e/objective_lifecycle.e2e.test.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { expect, it } from "vitest"; +import { approveAll } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +const { copilotClient: client, workDir } = await createSdkTestContext(); + +it("reduces goal completion before idle and pauses a goal when leaving autopilot", async () => { + const config = { + onPermissionRequest: approveAll, + workingDirectory: workDir, + remoteSession: "off" as const, + }; + const completed = await client.createSession(config); + const goal = await completed.rpc.commands.invoke({ + name: "goal", + input: "Call task_complete immediately with summary 'The release is ready.' Do not call other tools or perform any work.", + }); + if (goal.kind !== "agent-prompt") throw new Error(`Unexpected goal result: ${goal.kind}`); + await completed.sendAndWait({ prompt: goal.prompt, agentMode: "autopilot" }); + expect((await completed.rpc.autopilotObjective.getState()).state).toMatchObject({ + status: "completed", + turnCount: 1, + }); + + const paused = await client.createSession(config); + await paused.rpc.commands.invoke({ name: "goal", input: "Prepare another release" }); + await paused.rpc.mode.set({ mode: "autopilot" }); + await paused.rpc.mode.set({ mode: "interactive" }); + expect((await paused.rpc.autopilotObjective.getState()).state).toMatchObject({ + status: "paused", + pauseReason: "autopilot_off", + }); +}); diff --git a/nodejs/test/e2e/pending_work_resume.e2e.test.ts b/nodejs/test/e2e/pending_work_resume.e2e.test.ts index b4bf6fa352..ebe0889f76 100644 --- a/nodejs/test/e2e/pending_work_resume.e2e.test.ts +++ b/nodejs/test/e2e/pending_work_resume.e2e.test.ts @@ -150,7 +150,6 @@ describe("Pending work resume", async () => { const server = createClient({ workingDirectory: workDir, env, - gitHubToken: DEFAULT_GITHUB_TOKEN, connection: RuntimeConnection.forTcp({ path: process.env.COPILOT_CLI_PATH, connectionToken: SHARED_TOKEN, @@ -308,6 +307,122 @@ describe("Pending work resume", async () => { } ); + it( + "should continue an approved permission after abandoning an earlier pending permission", + { timeout: TEST_TIMEOUT_MS }, + async () => { + const client = createClient(); + onTestFinished(async () => { + await client.stop(); + }); + const executions: string[] = []; + const config = { + tools: [ + defineTool("resume_permission_tool", { + description: "Transforms a value after permission is granted", + parameters: z.object({ value: z.string() }), + handler: ({ value }) => { + executions.push(value); + return `PERMISSION_RESUMED_${value.toUpperCase()}`; + }, + }), + ], + onPermissionRequest: (): PermissionRequestResult => ({ kind: "no-result" }), + infiniteSessions: { enabled: false }, + }; + const original = await client.createSession(config); + expect((await original.rpc.permissions.setRequired({ required: true })).success).toBe( + true + ); + const abandonedPermission = waitForPermissionRequest(original); + await original.send({ + prompt: "Use resume_permission_tool with value 'alpha', then reply with the result.", + }); + const abandoned = await abandonedPermission; + await original.disconnect(); + + const next = await client.resumeSession(original.sessionId, { + ...config, + continuePendingWork: false, + }); + expect((await next.rpc.permissions.pendingRequests()).items).toEqual([]); + expect( + ( + await next.rpc.permissions.handlePendingPermissionRequest({ + requestId: abandoned.data.requestId, + result: { kind: "approve-once" }, + }) + ).success + ).toBe(false); + expect((await next.rpc.permissions.setRequired({ required: true })).success).toBe(true); + const nextPermission = waitForPermissionRequest(next); + await next.send({ + prompt: "Use resume_permission_tool with value 'beta', then reply with the result.", + }); + const pending = await nextPermission; + await next.disconnect(); + + const resumed = await client.resumeSession(original.sessionId, { + ...config, + continuePendingWork: true, + }); + expect((await resumed.rpc.permissions.setRequired({ required: true })).success).toBe( + true + ); + expect( + (await resumed.rpc.permissions.pendingRequests()).items.map( + (request) => request.requestId + ) + ).toEqual([pending.data.requestId]); + const resumeEvents = (await resumed.getEvents()).filter( + (event) => event.type === "session.resume" + ); + expect( + resumeEvents.map((event) => ({ + continuePendingWork: event.data.continuePendingWork, + sessionWasActive: event.data.sessionWasActive, + })) + ).toEqual([ + { continuePendingWork: false, sessionWasActive: false }, + { continuePendingWork: true, sessionWasActive: false }, + ]); + + const idle = deferred(); + const replies: string[] = []; + const unsubscribe = resumed.on((event) => { + if (event.agentId) return; + if (event.type === "assistant.message") { + replies.push(event.data.content); + } else if (event.type === "session.idle") { + idle.resolve(); + } else if (event.type === "session.error") { + idle.reject(new Error(event.data.message)); + } + }); + onTestFinished(unsubscribe); + expect( + ( + await resumed.rpc.permissions.handlePendingPermissionRequest({ + requestId: pending.data.requestId, + result: { kind: "approve-once" }, + }) + ).success + ).toBe(true); + await waitWithTimeout(idle.promise, PENDING_WORK_TIMEOUT_MS, "approved turn idle"); + expect(replies.join("\n")).toContain("PERMISSION_RESUMED_BETA"); + expect(executions).toEqual(["beta"]); + expect( + (await resumed.getEvents()).filter( + (event) => + event.type === "tool.execution_complete" && + event.data.toolCallId === pending.data.permissionRequest.toolCallId + ) + ).toMatchObject([{ data: { success: true } }]); + expect((await resumed.rpc.permissions.pendingRequests()).items).toEqual([]); + await resumed.disconnect(); + } + ); + it( "should continue pending external tool request after resume", { timeout: TEST_TIMEOUT_MS }, @@ -602,18 +717,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/plugin_git_auth.e2e.test.ts b/nodejs/test/e2e/plugin_git_auth.e2e.test.ts new file mode 100644 index 0000000000..be3c6f3005 --- /dev/null +++ b/nodejs/test/e2e/plugin_git_auth.e2e.test.ts @@ -0,0 +1,236 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { execFileSync, spawn, type ChildProcess } from "node:child_process"; +import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises"; +import { createServer } from "node:https"; +import { join } from "node:path"; +import { describe, it } from "vitest"; +import { approveAll, CopilotClient, RuntimeConnection } from "../../src/index.js"; +import { createIdentityForHost, generateCA } from "../../../test/harness/certUtils.js"; +import { createSdkTestContext, DEFAULT_GITHUB_TOKEN } from "./harness/sdkTestContext.js"; + +// Rust covers Git retry decisions; this exercises SDK-owned credentials across RPC. +describe("Plugin Git authentication fallback", async () => { + const { workDir, env } = await createSdkTestContext({ + copilotClientOptions: { gitHubToken: DEFAULT_GITHUB_TOKEN }, + }); + + it( + "clones and refreshes a private marketplace with the signed-in token", + { timeout: 90_000 }, + async (ctx) => { + const home = join(workDir, "auth-home"); + const source = join(workDir, "source"); + await mkdir(home); + await mkdir(source); + const configPath = join(home, "gitconfig"); + const tracePath = join(home, "git-trace.jsonl"); + await writeFile(configPath, ""); + const gitEnv = { + ...env, + GIT_CONFIG_GLOBAL: configPath, + GIT_CONFIG_NOSYSTEM: "1", + GIT_CONFIG_COUNT: "0", + GIT_TERMINAL_PROMPT: "0", + GIT_TRACE_REDACT: "0", + GIT_TRACE2_EVENT: tracePath, + GIT_TRACE2_ENV_VARS: "GIT_CONFIG_VALUE_1,GIT_CONFIG_VALUE_2,GIT_CONFIG_VALUE_3", + GIT_TRACE2_CONFIG_PARAMS: "http.*.extraheader", + }; + const git = (...args: string[]) => + execFileSync("git", args, { cwd: source, env: gitEnv, windowsHide: true }); + await access( + join( + git("--exec-path").toString().trim(), + process.platform === "win32" ? "git-http-backend.exe" : "git-http-backend" + ) + ); + git("init", "--quiet"); + git("config", "user.name", "SDK Test"); + git("config", "user.email", "sdk-test@example.com"); + await writeFile( + join(source, "marketplace.json"), + JSON.stringify({ + name: "private-auth-marketplace", + owner: { name: "SDK Test" }, + plugins: [], + }) + ); + git("add", "marketplace.json"); + git("commit", "--quiet", "-m", "fixture"); + git("clone", "--quiet", "--bare", ".", "../repo.git"); + + const ca = generateCA(); + const identity = createIdentityForHost("127.0.0.1", ca); + const caPath = join(home, "ca.pem"); + await writeFile(caPath, ca.certPem); + const authorization = `Basic ${Buffer.from(`x-access-token:${DEFAULT_GITHUB_TOKEN}`).toString("base64")}`; + const observed: Array = []; + const failures: Error[] = []; + const backends = new Set(); + const server = createServer( + { key: identity.keyPem, cert: identity.certPem }, + (request, response) => { + observed.push(request.headers.authorization); + if (request.headers.authorization !== authorization) { + response.writeHead(401, { + "WWW-Authenticate": 'Basic realm="marketplace"', + }); + response.end(); + return; + } + const target = new URL(request.url!, "https://127.0.0.1"); + const backend = spawn("git", ["http-backend"], { + cwd: workDir, + env: { + ...gitEnv, + GIT_PROJECT_ROOT: workDir, + GIT_HTTP_EXPORT_ALL: "1", + PATH_INFO: target.pathname, + QUERY_STRING: target.search.slice(1), + REQUEST_METHOD: request.method, + CONTENT_TYPE: request.headers["content-type"], + REMOTE_USER: "sdk-test", + }, + windowsHide: true, + stdio: ["pipe", "pipe", "pipe"], + }); + backends.add(backend); + const chunks: Buffer[] = []; + const stderr: Buffer[] = []; + backend.stdout!.on("data", (chunk: Buffer) => chunks.push(chunk)); + backend.stderr!.on("data", (chunk: Buffer) => stderr.push(chunk)); + backend.on("error", (error) => { + failures.push(error); + response.destroy(error); + }); + backend.on("close", (code) => { + backends.delete(backend); + if (code !== 0) { + const error = new Error( + `git http-backend exited ${code}: ${Buffer.concat(stderr)}` + ); + failures.push(error); + response.destroy(error); + return; + } + const output = Buffer.concat(chunks); + const boundary = output.indexOf("\r\n\r\n"); + if (boundary < 0) { + const error = new Error("git http-backend returned no CGI headers"); + failures.push(error); + response.destroy(error); + return; + } + for (const header of output + .subarray(0, boundary) + .toString() + .split("\r\n")) { + const colon = header.indexOf(":"); + response.setHeader( + header.slice(0, colon), + header.slice(colon + 1).trim() + ); + } + response.end(output.subarray(boundary + 4)); + }); + request.pipe(backend.stdin!); + } + ); + let client: CopilotClient | undefined; + ctx.onTestFinished(async () => { + try { + await client?.stop(); + } finally { + server.closeAllConnections(); + if (server.listening) { + await new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())) + ); + } + for (const backend of backends) { + backend.kill(); + } + } + }); + 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("HTTPS fixture has no TCP address"); + } + const host = `https://127.0.0.1:${address.port}`; + await writeFile( + configPath, + `[http]\nsslCAInfo = ${caPath.replaceAll("\\", "/")}\nproxy =\n` + + // The fixture CA has no revocation service; retain certificate verification. + (process.platform === "win32" + ? "sslBackend = schannel\nschannelUseSSLCAInfo = true\nschannelCheckRevoke = false\n" + : "") + ); + client = new CopilotClient({ + workingDirectory: workDir, + connection: RuntimeConnection.forStdio({ path: process.env.COPILOT_CLI_PATH }), + gitHubToken: DEFAULT_GITHUB_TOKEN, + env: { + ...gitEnv, + GIT_SSL_CAINFO: caPath, + COPILOT_HOME: home, + COPILOT_CACHE_HOME: join(home, "cache"), + COPILOT_GH_HOST: host, + GH_HOST: "", + GH_CONFIG_DIR: home, + }, + logLevel: "error", + }); + await client.start(); + ctx.expect((await client.getAuthStatus()).host).toBe(host); + const remote = `${host}/repo.git`; + const added = await client.rpc.plugins.marketplaces.add({ source: remote }); + ctx.expect(added.name).toBe("private-auth-marketplace"); + const refreshed = await client.rpc.plugins.marketplaces.refresh({ + name: "private-auth-marketplace", + }); + ctx.expect(refreshed.results).toEqual([ + { name: "private-auth-marketplace", success: true }, + ]); + const session = await client.createSession({ onPermissionRequest: approveAll }); + try { + const sessionRefresh = await session.rpc.plugins.marketplaces.refresh({ + name: "private-auth-marketplace", + }); + ctx.expect(sessionRefresh.results).toEqual([ + { name: "private-auth-marketplace", success: true }, + ]); + } finally { + await session.disconnect(); + } + ctx.expect(failures).toEqual([]); + ctx.expect(observed[0]).toBeUndefined(); + ctx.expect(observed).toContain(authorization); + const marketplace = (await client.rpc.plugins.marketplaces.list()).marketplaces.find( + (entry) => entry.name === "private-auth-marketplace" + ); + ctx.expect(marketplace?.source).toBe(`URL: ${remote}`); + const cache = join(home, "cache", "marketplaces"); + const directories = (await readdir(cache, { withFileTypes: true })).filter((entry) => + entry.isDirectory() + ); + ctx.expect(directories).toHaveLength(1); + const persisted = await readFile( + join(cache, directories[0].name, ".git", "config"), + "utf8" + ); + ctx.expect(persisted).toContain(remote); + ctx.expect(persisted).not.toContain(DEFAULT_GITHUB_TOKEN); + ctx.expect(persisted).not.toContain("extraheader"); + const trace = await readFile(tracePath, "utf8"); + ctx.expect(trace).not.toContain(DEFAULT_GITHUB_TOKEN); + ctx.expect(trace).not.toContain(authorization); + } + ); +}); 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 0b8049225e..e72eb5b656 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,7 @@ describe("Server-scoped RPC", async () => { const policy = { model: "auto", autoTier: "balance", + vscode: { temporaryDefaultToLocalHarness: true }, permissions: { limitTo: ["Domain(*.example.com)"], deny: ["Domain(blocked.example.com)"], @@ -124,6 +126,7 @@ describe("Server-scoped RPC", async () => { model: "gpt-5", effortLevel: { overridable: "high" }, contextTier: "long_context", + vscode: { temporaryDefaultToLocalHarness: false }, }; fs.writeFileSync(policyPath, JSON.stringify(policy)); const policyClient = new CopilotClient({ @@ -149,6 +152,10 @@ describe("Server-scoped RPC", async () => { const schema = await api.schema(); expect(schema.runtimeVersion).toEqual(expect.any(String)); expect(schema.schema).toHaveProperty("properties.model"); + expect(schema.schema).toHaveProperty( + "properties.vscode.properties.temporaryDefaultToLocalHarness.type", + "boolean" + ); const validated = await api.validate({ content: policy }); expect(validated.valid).toBe(true); @@ -163,6 +170,7 @@ describe("Server-scoped RPC", async () => { expect(resolved.account).toBeUndefined(); expect(resolved.resolved.deviceManaged).toBe(true); expect(resolved.values?.model).toBe(policy.model); + expect(resolved.resolved.settings).toHaveProperty("vscode", policy.vscode); const device = resolved.layers.find((layer) => layer.source === "device"); expect(device?.settings).toMatchObject(policy); @@ -223,6 +231,9 @@ describe("Server-scoped RPC", async () => { model: "auto", }); onTestFinished(() => managedSession.disconnect()); + await expect(managedSession.rpc.managedSettings.get()).resolves.toMatchObject({ + settings: { vscode: policy.vscode }, + }); await expect .poll(() => managedSession.rpc.model.getCurrent()) .toMatchObject({ modelId: "auto", autoTier: "balance" }); @@ -240,11 +251,7 @@ describe("Server-scoped RPC", async () => { const concretePolicyPath = path.join(workDir, "managed-concrete-settings.json"); fs.writeFileSync( concretePolicyPath, - JSON.stringify({ - model: "gpt-5", - effortLevel: "high", - contextTier: "long_context", - }) + JSON.stringify({ ...concretePolicy, effortLevel: "high" }) ); const concretePolicyClient = new CopilotClient({ workingDirectory: workDir, @@ -276,6 +283,7 @@ describe("Server-scoped RPC", async () => { model: "gpt-5", effortLevel: "high", contextTier: "long_context", + vscode: concretePolicy.vscode, }, }); await expect @@ -294,6 +302,9 @@ describe("Server-scoped RPC", async () => { enforceManagedModelDefaults: false, }); onTestFinished(() => relaxedSession.disconnect()); + await expect(relaxedSession.rpc.managedSettings.get()).resolves.toMatchObject({ + settings: { vscode: concretePolicy.vscode }, + }); await expect( relaxedSession.rpc.model.setReasoningEffort({ reasoningEffort: "low" }) ).resolves.toMatchObject({ reasoningEffort: "low" }); @@ -514,6 +525,119 @@ describe("Server-scoped RPC", async () => { } }, 60_000); + it("should resolve lifecycle-only session prefixes in the configured home", async () => { + const sessionId = randomUUID(); + const home = createUniqueWorkDirectory("prefix-home"); + const sessionDirectory = path.join(home, "session-state", sessionId); + fs.mkdirSync(sessionDirectory, { recursive: true }); + fs.writeFileSync( + path.join(sessionDirectory, "events.jsonl"), + `${JSON.stringify({ type: "session.start", data: { sessionId } })}\n` + ); + const prefixClient = createClientWithEnv({ COPILOT_HOME: home }); + await prefixClient.start(); + + expect( + await prefixClient.rpc.sessions.findByPrefix({ prefix: sessionId.slice(0, 8) }) + ).toEqual({ sessionId }); + const listed = await prefixClient.rpc.sessions.list({}); + expect(listed.sessions.map((session) => session.sessionId)).not.toContain(sessionId); + const defaultHomeClient = createClientWithEnv({}); + await defaultHomeClient.start(); + expect( + await defaultHomeClient.rpc.sessions.findByPrefix({ prefix: sessionId.slice(0, 8) }) + ).toEqual({}); + }); + + it("should omit detached session prefixes", async () => { + const home = createUniqueWorkDirectory("detached-prefix-home"); + const prefixClient = createClientWithEnv({ COPILOT_HOME: home }); + await prefixClient.start(); + + for (const detachedKind of ["marker", "event"] as const) { + const sessionId = randomUUID(); + const sessionDirectory = path.join(home, "session-state", sessionId); + fs.mkdirSync(sessionDirectory, { recursive: true }); + const eventsPath = path.join(sessionDirectory, "events.jsonl"); + fs.writeFileSync( + eventsPath, + `${JSON.stringify({ type: "session.start", data: { sessionId } })}\n` + ); + expect( + await prefixClient.rpc.sessions.findByPrefix({ prefix: sessionId.slice(0, 8) }) + ).toEqual({ sessionId }); + fs.writeFileSync( + eventsPath, + `${JSON.stringify({ + type: "session.start", + data: { + sessionId, + ...(detachedKind === "event" + ? { detachedFromSpawningParentSessionId: randomUUID() } + : {}), + }, + })}\n` + ); + if (detachedKind === "marker") { + fs.writeFileSync(path.join(sessionDirectory, ".detached"), ""); + } + + expect( + await prefixClient.rpc.sessions.findByPrefix({ prefix: sessionId.slice(0, 8) }) + ).toEqual({}); + } + }); + + it("should omit linked session directories for exact and abbreviated prefixes", async () => { + const home = createUniqueWorkDirectory("linked-prefix-home"); + const outside = createUniqueWorkDirectory("linked-prefix-target"); + const transcript = `${JSON.stringify({ type: "session.start", data: {} })}\n`; + fs.writeFileSync(path.join(outside, "events.jsonl"), transcript); + const sessionsHome = path.join(home, "session-state"); + fs.mkdirSync(sessionsHome); + const prefixClient = createClientWithEnv({ COPILOT_HOME: home }); + await prefixClient.start(); + + const sessionId = randomUUID(); + const prefix = sessionId.slice(0, 8); + for (const linkedId of [prefix, sessionId]) { + const link = path.join(sessionsHome, linkedId); + fs.symlinkSync(outside, link, process.platform === "win32" ? "junction" : "dir"); + try { + expect(await prefixClient.rpc.sessions.findByPrefix({ prefix })).toEqual({}); + } finally { + fs.unlinkSync(link); + } + } + expect(fs.readFileSync(path.join(outside, "events.jsonl"), "utf8")).toBe(transcript); + }); + + it("should omit linked event files for exact and abbreviated prefixes", async () => { + const home = createUniqueWorkDirectory("linked-events-home"); + const outside = createUniqueWorkDirectory("linked-events-target"); + const target = path.join(outside, "events.jsonl"); + const transcript = `${JSON.stringify({ type: "session.start", data: {} })}\n`; + fs.writeFileSync(target, transcript); + const prefixClient = createClientWithEnv({ COPILOT_HOME: home }); + await prefixClient.start(); + + const sessionId = randomUUID(); + const prefix = sessionId.slice(0, 8); + for (const linkedId of [prefix, sessionId]) { + const sessionDirectory = path.join(home, "session-state", linkedId); + fs.mkdirSync(sessionDirectory, { recursive: true }); + const link = path.join(sessionDirectory, "events.jsonl"); + fs.symlinkSync(target, link, "file"); + try { + expect(await prefixClient.rpc.sessions.findByPrefix({ prefix })).toEqual({}); + } finally { + fs.unlinkSync(link); + fs.rmdirSync(sessionDirectory); + } + } + expect(fs.readFileSync(target, "utf8")).toBe(transcript); + }); + it("should enrich basic session metadata", async () => { const sessionId = randomUUID(); const workingDirectory = createUniqueWorkDirectory("server-rpc-enrich"); @@ -690,6 +814,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_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 5df5024b14..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 }); @@ -273,7 +286,7 @@ describe("Shell and fleet RPC", async () => { 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 }); @@ -294,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 1b71849db7..aea0df1ec6 100644 --- a/nodejs/test/e2e/rpc_surface_coverage.e2e.test.ts +++ b/nodejs/test/e2e/rpc_surface_coverage.e2e.test.ts @@ -327,7 +327,24 @@ describe("Generated RPC surface coverage", () => { ...collectRuntimeFunctions(session.rpc, "session"), ]); - expect(inventory).toHaveLength(407); + expect(inventory).toHaveLength(412); + expect( + inventory.filter((method) => method.wireMethod === "contentExclusion.validatePolicies") + ).toEqual([ + expect.objectContaining({ scope: "server", path: "contentExclusion.validatePolicies" }), + ]); + 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" }), ]); 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 e6e6a18c8f..f9600b990f 100644 --- a/nodejs/test/e2e/rpc_tasks_and_handlers.e2e.test.ts +++ b/nodejs/test/e2e/rpc_tasks_and_handlers.e2e.test.ts @@ -326,8 +326,7 @@ 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. + // Each attached-shell test owns its client; replay normalizes process-global shell IDs. async function withRunningAttachedShell( run: ( session: CopilotSession, diff --git a/nodejs/test/e2e/rpc_workspace_checkpoints.e2e.test.ts b/nodejs/test/e2e/rpc_workspace_checkpoints.e2e.test.ts index 83ba34434c..91bd822a71 100644 --- a/nodejs/test/e2e/rpc_workspace_checkpoints.e2e.test.ts +++ b/nodejs/test/e2e/rpc_workspace_checkpoints.e2e.test.ts @@ -62,10 +62,11 @@ describe("Session workspace checkpoint RPC", async () => { ); writeFileSync(join(gitWorkDir, modifiedName), "after"); unlinkSync(join(gitWorkDir, deletedName)); - const gitClient = createClient({ workingDirectory: gitWorkDir }); + const gitClient = createClient(); try { const session = await gitClient.createSession({ onPermissionRequest: approveAll, + workingDirectory: gitWorkDir, }); try { const result = await session.rpc.workspaces.diff({ mode }); 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 81309ee5a8..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, 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({ 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_hooks.e2e.test.ts b/nodejs/test/e2e/subagent_hooks.e2e.test.ts index 818560bd7f..84aff60450 100644 --- a/nodejs/test/e2e/subagent_hooks.e2e.test.ts +++ b/nodejs/test/e2e/subagent_hooks.e2e.test.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { writeFile } from "fs/promises"; +import { copyFile, mkdir, readFile, readdir, writeFile } from "fs/promises"; import { randomUUID } from "node:crypto"; import { join } from "path"; import { describe, expect, it } from "vitest"; @@ -98,7 +98,11 @@ describe("Subagent hooks", async () => { // For snapshot recording (non-CI), use RECORD_GH_TOKEN if available const recordToken = !isCI ? process.env.RECORD_GH_TOKEN : undefined; const requestHandler = new RecordingRequestHandler(); - const { copilotClient: client, workDir } = await createSdkTestContext({ + const { + copilotClient: client, + workDir, + env, + } = await createSdkTestContext({ copilotClientOptions: { ...(recordToken ? { gitHubToken: recordToken } : {}), requestHandler, @@ -109,7 +113,12 @@ describe("Subagent hooks", async () => { it( "should apply subagent lifecycle hook outputs", async () => { - const hookLog: { kind: "pre" | "post"; toolName: string; sessionId: string }[] = []; + const hookLog: { + kind: "pre" | "post"; + toolName: string; + sessionId: string; + input: PreToolUseHookInput | PostToolUseHookInput; + }[] = []; const startInputs: { input: SubagentStartHookInput; invocationSessionId: string }[] = []; const stopInputs: { input: SubagentStopHookInput; invocationSessionId: string }[] = []; @@ -118,6 +127,43 @@ describe("Subagent hooks", async () => { const finalText = "The explore agent successfully read the file. The contents of **subagent-test.txt** are:\n\n```\nHello from subagent test!\n```"; const parentSessionId = randomUUID(); + const capturesDir = join(workDir, "hook-inputs"); + const hooksDir = join(workDir, ".github", "hooks"); + await mkdir(capturesDir); + await mkdir(hooksDir, { recursive: true }); + await mkdir(env.COPILOT_HOME, { recursive: true }); + await writeFile( + join(env.COPILOT_HOME, "config.json"), + JSON.stringify({ trustedFolders: [workDir] }) + ); + await copyFile( + new URL("./fixtures/hook-identity-recorder.cjs", import.meta.url), + join(workDir, "hook-recorder.cjs") + ); + const repositoryHooks: Record = {}; + for (const [native, claude] of [ + ["preToolUse", "PreToolUse"], + ["postToolUse", "PostToolUse"], + ["subagentStart", "SubagentStart"], + ["subagentStop", "SubagentStop"], + ]) { + for (const [event, dialect] of [ + [native, "native"], + [claude, "claude"], + ]) { + repositoryHooks[event] = [ + { + type: "command", + exec: process.execPath, + args: ["hook-recorder.cjs", `${native}.${dialect}`, capturesDir], + }, + ]; + } + } + await writeFile( + join(hooksDir, "identity.json"), + JSON.stringify({ version: 1, hooks: repositoryHooks }) + ); let releaseView!: () => void; const parentWaiting = new Promise((resolve) => { releaseView = resolve; @@ -132,6 +178,7 @@ describe("Subagent hooks", async () => { kind: "pre", toolName: input.toolName, sessionId: input.sessionId, + input, }); return { permissionDecision: "allow" } as PreToolUseHookOutput; }, @@ -140,6 +187,7 @@ describe("Subagent hooks", async () => { kind: "post", toolName: input.toolName, sessionId: input.sessionId, + input, }); // A fast child can inject its result before the parent ever asks for the fixture's waiting reply. if (input.toolName === "view" && input.sessionId !== parentSessionId) @@ -238,6 +286,23 @@ describe("Subagent hooks", async () => { expect(stop!.input.agentDescription).toBe(start!.input.agentDescription); expect(stop!.input.agentId).toBeTruthy(); expect(stop!.input.agentType).toBe("explore"); + expect(start!.input).toMatchObject({ + agentId: stop!.input.agentId, + agentType: "explore", + parentAgentId: session.sessionId, + }); + for (const { input } of [...viewPre, ...viewPost]) { + expect(input).toMatchObject({ + agentId: stop!.input.agentId, + agentType: "explore", + parentAgentId: session.sessionId, + }); + } + for (const { input } of hookLog.filter((hook) => hook.sessionId === parentSessionId)) { + expect(input).not.toHaveProperty("agentId"); + expect(input).not.toHaveProperty("agentType"); + expect(input).not.toHaveProperty("parentAgentId"); + } expect(stop!.input.stopReason).toBe("end_turn"); expect(stop!.input.response).toContain("Hello from subagent test!"); @@ -252,6 +317,103 @@ describe("Subagent hooks", async () => { timeout: SUBAGENT_TEST_TIMEOUT_MS, }) .toBeGreaterThan(0); + const capturedInputs = await Promise.all( + (await readdir(capturesDir)).map(async (name) => { + return JSON.parse(await readFile(join(capturesDir, name), "utf8")) as { + hook: string; + input: Record; + }; + }) + ); + for (const dialect of ["native", "claude"]) { + const [idKey, typeKey, parentKey, toolKey, childTool, parentTool] = + dialect === "native" + ? ["agentId", "agentType", "parentAgentId", "toolName", "view", "task"] + : [ + "agent_id", + "agent_type", + "parent_agent_id", + "tool_name", + "Read", + "Agent", + ]; + for (const event of ["preToolUse", "postToolUse"]) { + const inputs = capturedInputs.filter( + (record) => record.hook === `${event}.${dialect}` + ); + const childInputs = inputs.filter( + (record) => record.input[toolKey] === childTool + ); + const parentInputs = inputs.filter( + (record) => record.input[toolKey] === parentTool + ); + expect( + childInputs, + `${event}/${dialect} must run in the child` + ).not.toHaveLength(0); + expect( + parentInputs, + `${event}/${dialect} must run in the parent` + ).not.toHaveLength(0); + for (const { input } of childInputs) { + expect(input).toMatchObject({ + [idKey]: stop!.input.agentId, + [typeKey]: "explore", + [parentKey]: parentSessionId, + }); + } + for (const { input } of parentInputs) { + expect(input).not.toHaveProperty(idKey); + expect(input).not.toHaveProperty(typeKey); + expect(input).not.toHaveProperty(parentKey); + } + } + for (const event of ["subagentStart", "subagentStop"]) { + const inputs = capturedInputs.filter( + (record) => record.hook === `${event}.${dialect}` + ); + expect(inputs, `${event}/${dialect} must describe the one child`).toHaveLength( + 1 + ); + expect(inputs[0].input).toMatchObject({ + [idKey]: stop!.input.agentId, + [typeKey]: "explore", + [parentKey]: parentSessionId, + }); + expect(inputs[0].input.sessionId ?? inputs[0].input.session_id).toBe( + parentSessionId + ); + } + } + const transcript = await session.getEvents(); + const hookStarts = transcript.filter( + (event) => + event.type === "hook.start" && + ["preToolUse", "postToolUse", "subagentStart", "subagentStop"].includes( + event.data.hookType + ) + ); + expect( + hookStarts.some( + (event) => + event.agentId === stop!.input.agentId && + event.data.hookType === "preToolUse" + ), + "the transcript must attribute child hooks to the same stable identity" + ).toBe(true); + for (const start of hookStarts) { + const end = transcript.find( + (event) => + event.type === "hook.end" && + event.data.hookInvocationId === start.data.hookInvocationId + ); + expect( + end, + `hook ${start.data.hookInvocationId} must have completed` + ).toMatchObject({ + data: { success: true }, + }); + } await session.disconnect(); }, SUBAGENT_TEST_TIMEOUT_MS diff --git a/nodejs/test/e2e/subagent_prompt_resume.e2e.test.ts b/nodejs/test/e2e/subagent_prompt_resume.e2e.test.ts index 00d0469647..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,6 +11,7 @@ import type { CopilotSession, CustomAgentConfig, MCPStdioServerConfig, + SessionEvent, } from "../../src/index.js"; import { approveAll, CopilotRequestHandler } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; @@ -248,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", + }, }, }); @@ -277,7 +282,7 @@ describe("Subagent prompt resume", async () => { beforeAgentStart: (session: CopilotSession) => Promise = async () => {}, agentType = "explore", agentModel: string | null = "claude-sonnet-5" - ): Promise<[InferenceRequest, InferenceRequest]> { + ): Promise<[InferenceRequest, InferenceRequest, string]> { await writeFile(join(env.COPILOT_HOME, "settings.json"), "{}"); const requestBaseline = requestHandler.inferenceRequests.length; const session = await client.createSession({ @@ -321,7 +326,7 @@ describe("Subagent prompt resume", async () => { .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) { @@ -357,16 +362,104 @@ 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 requests = await runResumeScenario( + const [initial, resumed] = await runResumeScenario( async () => {}, { customAgents: [ @@ -383,7 +476,7 @@ describe("Subagent prompt resume", async () => { null ); - for (const request of requests) { + for (const request of [initial, resumed]) { expect(request.model).toBe("gpt-5.4-mini"); expect(request.reasoningEffort).toBe(effort === "high" ? "high" : "medium"); } @@ -393,7 +486,7 @@ describe("Subagent prompt resume", async () => { capiIt( "reconciles an independently declared child effort without changing its model", async () => { - const requests = await runResumeScenario( + const [initial, resumed] = await runResumeScenario( async () => {}, { customAgents: [ @@ -411,7 +504,7 @@ describe("Subagent prompt resume", async () => { null ); - for (const request of requests) { + for (const request of [initial, resumed]) { expect(request.model).toBe("gpt-5.4-mini"); expect(request.reasoningEffort).toBe("medium"); } 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/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 1d30226112..e17eda6809 100644 --- a/nodejs/test/e2e/workiq_discovery.e2e.test.ts +++ b/nodejs/test/e2e/workiq_discovery.e2e.test.ts @@ -378,7 +378,10 @@ describe("account-derived WorkIQ", async () => { { timeoutMessage: "WorkIQ did not connect after enablement" } ); const initialGrantCount = authentication === "browser" ? 2 : 1; - expect(grants).toHaveLength(initialGrantCount); + expect( + grants.map((grant) => Object.fromEntries(grant)), + JSON.stringify({ requests, browserScopes }) + ).toHaveLength(initialGrantCount); expect(grants[0].get("client_id")).toBe("ba081686-5d24-4bc6-a0d6-d034ecffed87"); expect(grants[0].get("refresh_token")).toBe("parent-refresh"); expect(grants[0].get("scope")).toContain(scope); diff --git a/nodejs/test/hook-identity.test.ts b/nodejs/test/hook-identity.test.ts new file mode 100644 index 0000000000..ecf510e8d2 --- /dev/null +++ b/nodejs/test/hook-identity.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +import { describe, expect, it } from "vitest"; +import type { MessageConnection } from "vscode-jsonrpc/node.js"; +import { CopilotSession } from "../src/session.js"; +import type { BaseHookInput, SessionHooks } from "../src/types.js"; + +const hooks = [ + ["preToolUse", "onPreToolUse"], + ["preMcpToolCall", "onPreMcpToolCall"], + ["postToolUse", "onPostToolUse"], + ["postToolUseFailure", "onPostToolUseFailure"], + ["userPromptSubmitted", "onUserPromptSubmitted"], + ["userPromptTransformed", "onUserPromptTransformed"], + ["sessionStart", "onSessionStart"], + ["sessionEnd", "onSessionEnd"], + ["errorOccurred", "onErrorOccurred"], + ["agentStop", "onAgentStop"], + ["subagentStart", "onSubagentStart"], + ["subagentStop", "onSubagentStop"], +] as const; + +describe("typed hook identity", () => { + for (const [hook, handler] of hooks) { + for (const enriched of [true, false]) { + it(`${hook} preserves ${enriched ? "child identity" : "old root input"}`, async () => { + const session = new CopilotSession("callback-owner", {} as MessageConnection); + const captured: BaseHookInput[] = []; + session.registerHooks({ + [handler]: (input: BaseHookInput, invocation: { sessionId: string }) => { + expect(invocation.sessionId).toBe("callback-owner"); + captured.push(input); + }, + } as SessionHooks); + const identity = { + agentId: "child-actor", + agentType: "explore", + parentAgentId: "parent-actor", + }; + await session._handleHooksInvoke(hook, { + sessionId: "reported-session", + timestamp: 1, + cwd: "work", + ...(enriched ? identity : {}), + }); + expect(captured).toHaveLength(1); + const input = captured[0]; + if (input === undefined) { + throw new Error("Hook callback was not dispatched"); + } + expect(input.sessionId).toBe("reported-session"); + expect(input.timestamp).toEqual(new Date(1)); + expect([input.agentId, input.agentType, input.parentAgentId]).toEqual( + enriched + ? ["child-actor", "explore", "parent-actor"] + : [undefined, undefined, undefined] + ); + const oldRoot: BaseHookInput = { + sessionId: "root", + timestamp: new Date(1), + workingDirectory: "work", + }; + expect(oldRoot.agentId).toBeUndefined(); + }); + } + } +}); 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 36cb7e3133..627f8ca52e 100644 --- a/nodejs/test/python-codegen.test.ts +++ b/nodejs/test/python-codegen.test.ts @@ -88,11 +88,6 @@ describe("Python RPC projection compatibility", () => { expect(code).toContain(`"${result}",`); }); - it("does not restore the retired Factory checkpoint aliases", () => { - expect(code).not.toContain("SessionFactoryPauseAtCheckpointResult"); - expect(code).not.toContain('"session.factory.pauseAtCheckpoint"'); - }); - it("keeps named connection-scoped host callback results", () => { expect(code).toContain( "async def materialize_session(self, params: HostSessionCreateCallback) -> HostSessionCreateResult:" 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 53c471b104..ce3ee6b426 100644 --- a/nodejs/test/rust-codegen.test.ts +++ b/nodejs/test/rust-codegen.test.ts @@ -821,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/shared-codegen.test.ts b/nodejs/test/shared-codegen.test.ts index a4698ac549..ca229ca01c 100644 --- a/nodejs/test/shared-codegen.test.ts +++ b/nodejs/test/shared-codegen.test.ts @@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url"; import { describe, expect, it, onTestFinished } from "vitest"; import { + addManagedApprovalRequiredToPermissionRequests, collectDefinitionCollections, collectExperimentalOnlyRpcReferencedDefinitionNames, collectReachableDefinitionNames, @@ -53,6 +54,38 @@ describe("codegen entrypoints", () => { }); describe("shared schema definition codegen utilities", () => { + it("adds managed approval metadata to workflow permission requests", () => { + const schema = { + definitions: { + PermissionRequestWorkflow: { + type: "object", + properties: { + kind: { const: "workflow" }, + }, + }, + PermissionRequestUnrelated: { + type: "object", + properties: { + kind: { const: "unrelated" }, + }, + }, + }, + } satisfies JSONSchema7; + + const result = addManagedApprovalRequiredToPermissionRequests(schema); + + expect(result.definitions?.PermissionRequestWorkflow).toMatchObject({ + properties: { + managedApprovalRequired: { + type: ["boolean", "null"], + }, + }, + }); + expect(result.definitions?.PermissionRequestUnrelated).not.toHaveProperty( + "properties.managedApprovalRequired" + ); + }); + it("shares open tier definitions across raw RPC and processed event schemas", () => { const tier = { type: "string", diff --git a/nodejs/test/toolSet.test.ts b/nodejs/test/toolSet.test.ts index b77b797072..8d3f9fa67b 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({ @@ -544,6 +565,38 @@ describe("Empty-mode safe defaults", () => { expect(resumePayload.enableSkills).toBe(false); }); + it("passes enableGitHubCacheStore through on create and resume and omits it when unset", async () => { + const { client, spy } = await setupClient("copilot-cli"); + const session = await client.createSession({ + onPermissionRequest: approveAll, + enableGitHubCacheStore: true, + }); + expect(createPayload(spy).enableGitHubCacheStore).toBe(true); + await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + enableGitHubCacheStore: false, + }); + const resumePayload = spy.mock.calls.find(([m]) => m === "session.resume")![1] as any; + expect(resumePayload.enableGitHubCacheStore).toBe(false); + + spy.mockClear(); + const unset = await client.createSession({ onPermissionRequest: approveAll }); + await client.resumeSession(unset.sessionId, { onPermissionRequest: approveAll }); + for (const method of ["session.create", "session.resume"]) { + const payload = spy.mock.calls.find(([m]) => m === method)![1] as any; + expect(payload.enableGitHubCacheStore).toBeUndefined(); + } + }); + + it("does not default enableGitHubCacheStore in empty mode", async () => { + const { client, spy } = await setupClient(); + await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated), + }); + expect(createPayload(spy).enableGitHubCacheStore).toBeUndefined(); + }); + it("forwards the four flags in copilot-cli mode when the app sets them", async () => { const { client, spy } = await setupClient("copilot-cli"); 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 9b1badf4ab..e672e9799f 100644 --- a/nodejs/vitest.config.ts +++ b/nodejs/vitest.config.ts @@ -3,57 +3,6 @@ import { isByokBackend } from "./test/e2e/harness/testBackend"; const integrationTestTimeout = process.platform === "win32" ? 60000 : 30000; -const isInProcessTransport = - (process.env.COPILOT_SDK_DEFAULT_CONNECTION ?? "").toLowerCase() === "inprocess"; - -// TODO(cli-1.0.81-2): CLI 1.0.81-5 still stops completing model-driven turns when -// hosted in-process against CAPI. The shared runtime then poisons every later -// model-driven test. These suites still run over stdio on all three OSes, while -// pure-RPC in-process coverage remains enabled. -const inProcessBlockedE2E = [ - "**/test/e2e/abort.e2e.test.ts", - "**/test/e2e/agent_and_compact_rpc.e2e.test.ts", - "**/test/e2e/ask_user.e2e.test.ts", - "**/test/e2e/builtin_tools.e2e.test.ts", - "**/test/e2e/client_api.e2e.test.ts", - "**/test/e2e/client_lifecycle.e2e.test.ts", - "**/test/e2e/compaction.e2e.test.ts", - "**/test/e2e/copilot_request_cancel_error.e2e.test.ts", - "**/test/e2e/copilot_request_handler.e2e.test.ts", - "**/test/e2e/copilot_request_session_id.e2e.test.ts", - "**/test/e2e/disabled_mcp_servers.e2e.test.ts", - "**/test/e2e/event_fidelity.e2e.test.ts", - "**/test/e2e/hooks.e2e.test.ts", - "**/test/e2e/hooks_extended.e2e.test.ts", - "**/test/e2e/mcp_and_agents.e2e.test.ts", - "**/test/e2e/mcp_instruction_recovery.e2e.test.ts", - "**/test/e2e/memory_retrieval.e2e.test.ts", - "**/test/e2e/mode_empty.e2e.test.ts", - "**/test/e2e/multi_turn.e2e.test.ts", - "**/test/e2e/permissions.e2e.test.ts", - "**/test/e2e/pre_mcp_tool_call_hook.e2e.test.ts", - "**/test/e2e/provider_endpoint.e2e.test.ts", - "**/test/e2e/rewind.e2e.test.ts", - "**/test/e2e/rpc_event_side_effects.e2e.test.ts", - "**/test/e2e/rpc_session_state.e2e.test.ts", - "**/test/e2e/rpc_session_state_extras.e2e.test.ts", - "**/test/e2e/rpc_shell_and_fleet.e2e.test.ts", - "**/test/e2e/session.e2e.test.ts", - "**/test/e2e/session_config.e2e.test.ts", - "**/test/e2e/session_fs.e2e.test.ts", - "**/test/e2e/session_fs_sqlite.e2e.test.ts", - "**/test/e2e/session_lifecycle.e2e.test.ts", - "**/test/e2e/session_todos_changed.e2e.test.ts", - "**/test/e2e/skills.e2e.test.ts", - "**/test/e2e/streaming_fidelity.e2e.test.ts", - "**/test/e2e/subagent_hooks.e2e.test.ts", - "**/test/e2e/suspend.e2e.test.ts", - "**/test/e2e/system_message_sections.e2e.test.ts", - "**/test/e2e/system_message_transform.e2e.test.ts", - "**/test/e2e/tool_results.e2e.test.ts", - "**/test/e2e/tools.e2e.test.ts", -]; - // These suites own their provider or CAPI auth/request-handler setup. They run // in the ordinary CAPI job, not as repetitions of the matrix-selected backend. const selfConfiguredBackendE2E = [ @@ -69,6 +18,7 @@ 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", ]; @@ -90,7 +40,6 @@ export default defineConfig({ "**/dist/**", "**/*.d.ts", "**/basic-test.ts", // Old manual test - ...(isInProcessTransport ? inProcessBlockedE2E : []), ...(isByokBackend ? selfConfiguredBackendE2E : []), ], }, diff --git a/python/README.md b/python/README.md index 91f433dc9c..6c63928da1 100644 --- a/python/README.md +++ b/python/README.md @@ -441,10 +441,12 @@ These are passed as keyword arguments to `create_session()`: - `infinite_sessions` (InfiniteSessionConfig): Automatic context compaction configuration - `working_directory` (str | None): Working directory for the session (default: runtime process working directory). - `enable_session_store` (bool): Enables the cross-session store for search and retrieval across sessions. When unset in `"copilot-cli"` mode, the runtime default applies (enabled). In `"empty"` mode, defaults to disabled. +- `enable_github_cache_store` (bool): Requests the read-only GitHub cache store (`github-cache-store` capability) on create or resume. The runtime registers the `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is also on. Defaults to disabled in every mode. - `github_token_provider` (callable): Acquires rotating, session-scoped GitHub tokens. Token results require a positive `expiresIn` value in seconds remaining when the callback completes; production tokens typically last eight hours. Cannot be combined with `github_token`. - `on_permission_request` (callable): Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `PermissionHandler.approve_all` approves requests when managed settings are disabled and raises an error when `enable_managed_settings` is true. Custom handlers can inspect `managed_approval_required` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `on_user_input_request` (callable): Handler for legacy question-and-answer requests from the agent. Enables the legacy `ask_user` tool. See [User Input Requests](#user-input-requests) section. - `ask_user_variant` (`"legacy"` | `"elicitation"`): Selects the model-facing shape of the `ask_user` tool. Defaults to `"legacy"`; use `"elicitation"` with `on_elicitation_request`. Re-supply this option when cold-resuming a session. +- `interactive_user` (bool): Declares that a person types this session's messages, as in an editor chat panel. Enables the runtime's user-message analysis telemetry for user-authored messages; automated messages stay excluded. Defaults to `False`. Ignored for cloud sessions. Re-supply it when cold-resuming a session; it is ignored when resuming a session that is already active. - `hooks` (SessionHooks): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. ```python diff --git a/python/copilot/client.py b/python/copilot/client.py index 89a9f940f6..8113c400e5 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2355,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, @@ -2404,10 +2405,13 @@ async def create_session( enable_file_hooks: bool | None = None, enable_host_git_operations: bool | None = None, enable_session_store: bool | None = None, + enable_github_cache_store: bool | None = None, enable_skills: bool | None = None, 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, @@ -2438,6 +2442,7 @@ async def create_session( exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, enforce_managed_model_defaults: bool | None = None, + interactive_user: bool | None = None, github_mcp_tool_config: GitHubMcpToolConfig | None = None, managed_settings: ManagedSettings | None = None, ) -> CopilotSession: @@ -2454,6 +2459,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. @@ -2554,18 +2565,36 @@ 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_github_cache_store: Requests the read-only GitHub cache store + (``github-cache-store`` capability). The runtime registers the + ``github_sql`` tool only when its ``GITHUB_CACHE_STORE`` feature flag + is also on. Defaults to disabled. 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 @@ -2623,6 +2652,10 @@ async def create_session( controls are enforced for this session. Conflicting model changes are rejected, while overridable managed defaults remain mutable. Sent on the wire as ``enforceManagedModelDefaults``. + interactive_user: Declares that a human drives this session's + conversation, enabling user-message analysis telemetry for + user-authored messages. Automated messages remain excluded. + Defaults to False for SDK sessions. Ignored for cloud sessions. managed_settings: Host-injected enterprise managed settings for the session. Supplies managed policy directly instead of self-fetching; the runtime validates it and composes it @@ -2786,6 +2819,9 @@ async def create_session( if enforce_managed_model_defaults is not None: payload["enforceManagedModelDefaults"] = enforce_managed_model_defaults + if interactive_user is not None: + payload["interactiveUser"] = interactive_user + # Host-injected managed settings (permissions-only contract) if managed_settings is not None: payload["managedSettings"] = _managed_settings_to_dict(managed_settings) @@ -2894,14 +2930,20 @@ async def create_session( payload["enableHostGitOperations"] = enable_host_git_operations if enable_session_store is not None: payload["enableSessionStore"] = enable_session_store + if enable_github_cache_store is not None: + payload["enableGitHubCacheStore"] = enable_github_cache_store if enable_skills is not None: payload["enableSkills"] = enable_skills if skill_provider is not None: 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: @@ -2969,6 +3011,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 ) @@ -3288,10 +3332,13 @@ async def resume_session( enable_file_hooks: bool | None = None, enable_host_git_operations: bool | None = None, enable_session_store: bool | None = None, + enable_github_cache_store: bool | None = None, enable_skills: bool | None = None, 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, @@ -3325,6 +3372,7 @@ async def resume_session( exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, enforce_managed_model_defaults: bool | None = None, + interactive_user: bool | None = None, github_mcp_tool_config: GitHubMcpToolConfig | None = None, managed_settings: ManagedSettings | None = None, ) -> CopilotSession: @@ -3436,17 +3484,35 @@ 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_github_cache_store: Requests the read-only GitHub cache store + (``github-cache-store`` capability). The runtime registers the + ``github_sql`` tool only when its ``GITHUB_CACHE_STORE`` feature flag + is also on. Defaults to disabled. 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 @@ -3511,6 +3577,10 @@ async def resume_session( controls are enforced after resume. Conflicting model changes are rejected, while overridable managed defaults remain mutable. Sent on the wire as ``enforceManagedModelDefaults``. + interactive_user: Re-supplies that a human drives this session so a + session loaded from storage keeps the declaration. It is not + persisted and is ignored if the session is already active in the + runtime process. managed_settings: Host-injected enterprise managed settings for the session. Must be re-supplied on resume; it replaces the prior injected layer, and omitting it clears that layer so warm and @@ -3692,6 +3762,9 @@ async def resume_session( if enforce_managed_model_defaults is not None: payload["enforceManagedModelDefaults"] = enforce_managed_model_defaults + if interactive_user is not None: + payload["interactiveUser"] = interactive_user + # Host-injected managed settings (permissions-only contract) if managed_settings is not None: payload["managedSettings"] = _managed_settings_to_dict(managed_settings) @@ -3716,6 +3789,8 @@ async def resume_session( payload["enableHostGitOperations"] = enable_host_git_operations if enable_session_store is not None: payload["enableSessionStore"] = enable_session_store + if enable_github_cache_store is not None: + payload["enableGitHubCacheStore"] = enable_github_cache_store if enable_skills is not None: payload["enableSkills"] = enable_skills if skill_provider is not None: @@ -3757,8 +3832,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: @@ -4563,6 +4642,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: diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 605a606bfd..bc4a4e2ddb 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -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: @@ -1585,6 +1704,17 @@ class CardDigestAlgorithm(Enum): SHA256_RFC8785 = "sha256-rfc8785" +class CatalogAgentPluginInstallSourceKind(Enum): + """Discriminator for a materialisation install source + + Discriminator for a marketplace install source + + Discriminator for a repository install source + """ + MARKETPLACE = "marketplace" + MATERIALIZATION = "materialization" + REPOSITORY = "repository" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CatalogPluginRepositorySource: @@ -1675,6 +1805,24 @@ class CatalogTrustTier(Enum): T1 = "T1" T2 = "T2" +# Experimental: this type is part of an experimental API and may change or be removed. +class CatalogAgentPluginMarketplaceSourceKind(Enum): + """Discriminator for a marketplace install source""" + + MARKETPLACE = "marketplace" + +# Experimental: this type is part of an experimental API and may change or be removed. +class CatalogAgentPluginMaterializationSourceKind(Enum): + """Discriminator for a materialisation install source""" + + MATERIALIZATION = "materialization" + +# Experimental: this type is part of an experimental API and may change or be removed. +class CatalogAgentPluginRepositorySourceKind(Enum): + """Discriminator for a repository install source""" + + REPOSITORY = "repository" + # Experimental: this type is part of an experimental API and may change or be removed. class CatalogAISkillInstallability(Enum): """Whether this AI skill candidate can be planned for verified installation in the selected @@ -1722,23 +1870,6 @@ class CatalogAuthenticationRequiredReason(Enum): CREDENTIAL_REJECTED = "credential-rejected" NO_CREDENTIAL = "no-credential" -class CatalogInstallability(Enum): - """Whether this MCP server can be planned for installation, and if policy prevents it. - - Whether an MCP server candidate can be planned for installation - - Whether this AI skill candidate can be planned for verified installation in the selected - session. - - Typed installability state for an AI skill candidate - """ - FEATURE_DISABLED = "feature-disabled" - INSTALLABLE = "installable" - MATERIALISATION_UNAVAILABLE = "materialisation-unavailable" - NOT_INSTALLABLE_KIND = "not-installable-kind" - NOT_INSTALLABLE_POLICY = "not-installable-policy" - POLICY_FORBIDS = "policy-forbids" - # Experimental: this type is part of an experimental API and may change or be removed. class MCPServerCardEmbeddedKind(Enum): """Discriminator for an embedded MCP server card""" @@ -1757,7 +1888,9 @@ class CatalogCapability(Enum): grant means the runtime understands the feature's contract, not that the deployment has enabled the operation; typed unavailable results report availability separately. """ + AGENT_PLUGIN_CONFIRMED_INSTALLATION = "agent-plugin-confirmed-installation" AGENT_PLUGIN_DISCOVERY = "agent-plugin-discovery" + AGENT_PLUGIN_FORMATS = "agent-plugin-formats" AI_SKILL_DISCOVERY = "ai-skill-discovery" CATALOG_SEARCH_CREDENTIAL_REQUIRED = "catalog-search-credential-required" CATALOG_SEARCH_PAGINATION = "catalog-search-pagination" @@ -1770,6 +1903,7 @@ class CatalogCapability(Enum): MCP_SERVER_CARD = "mcp-server-card" MULTIPLE_TRANSPORT_CHOICE = "multiple-transport-choice" SKILL_CONFIRMED_INSTALLATION = "skill-confirmed-installation" + SKILL_REFERENCE_INSTALLATION = "skill-reference-installation" TRUST_SNAPSHOT = "trust-snapshot" class CatalogContractViolationErrorKind(Enum): @@ -2869,6 +3003,49 @@ def to_dict(self) -> 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 ContentExclusionValidatePoliciesRequest: + """Candidate content-exclusion policies to validate, as the raw JSON text a host received. + The text is supplied unparsed so that a syntax error and a schema violation are both + reported by the runtime's own validator rather than by the caller's JSON parser. + """ + policies_json: str + """Unparsed JSON text expected to hold an array of content-exclusion policies.""" + + @staticmethod + def from_dict(obj: Any) -> 'ContentExclusionValidatePoliciesRequest': + assert isinstance(obj, dict) + policies_json = from_str(obj.get("policiesJson")) + return ContentExclusionValidatePoliciesRequest(policies_json) + + def to_dict(self) -> dict: + result: dict = {} + result["policiesJson"] = from_str(self.policies_json) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ContentExclusionValidatePoliciesResult: + """The accepted policies, parsed and normalized. A rejected input is reported as an error + rather than in this result. + """ + policies: list[Any] + """Validated policy objects in input order. Raw JSON preserves every field returned by the + runtime's policy parser. + """ + + @staticmethod + def from_dict(obj: Any) -> 'ContentExclusionValidatePoliciesResult': + assert isinstance(obj, dict) + policies = from_list(lambda x: x, obj.get("policies")) + return ContentExclusionValidatePoliciesResult(policies) + + def to_dict(self) -> dict: + result: dict = {} + result["policies"] = from_list(lambda x: x, self.policies) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class ContentFilterMode(Enum): """Controls how MCP tool result content is filtered: none leaves content unchanged, markdown @@ -4142,6 +4319,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 +4613,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 +4867,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 +4876,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 +5540,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 +5557,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 +6460,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 +6526,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 +6542,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 +6565,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 +6580,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 +6936,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 @@ -6553,6 +7133,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. @@ -6758,6 +7418,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: @@ -6786,6 +7626,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: @@ -6827,6 +7766,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: @@ -7332,7 +8384,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 @@ -7341,10 +8393,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 = {} @@ -7754,6 +8806,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: @@ -8322,17 +9396,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 MCPRegistryCancelRequest: +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': + def from_dict(obj: Any) -> '_MCPRegistryCancelRequest': assert isinstance(obj, dict) request_id = from_int(obj.get("requestId")) - return MCPRegistryCancelRequest(request_id) + return _MCPRegistryCancelRequest(request_id) def to_dict(self) -> dict: result: dict = {} @@ -8342,7 +9416,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 MCPRegistryCancelResult: +class _MCPRegistryCancelResult: """Whether the cancel reached a live search.""" canceled: bool @@ -8352,10 +9426,10 @@ class MCPRegistryCancelResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPRegistryCancelResult': + def from_dict(obj: Any) -> '_MCPRegistryCancelResult': assert isinstance(obj, dict) canceled = from_bool(obj.get("canceled")) - return MCPRegistryCancelResult(canceled) + return _MCPRegistryCancelResult(canceled) def to_dict(self) -> dict: result: dict = {} @@ -8365,7 +9439,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 MCPRegistryRequestIDResult: +class _MCPRegistryRequestIDResult: """Request id naming a cancellable registry search.""" request_id: int @@ -8374,10 +9448,10 @@ class MCPRegistryRequestIDResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPRegistryRequestIDResult': + def from_dict(obj: Any) -> '_MCPRegistryRequestIDResult': assert isinstance(obj, dict) request_id = from_int(obj.get("requestId")) - return MCPRegistryRequestIDResult(request_id) + return _MCPRegistryRequestIDResult(request_id) def to_dict(self) -> dict: result: dict = {} @@ -8387,7 +9461,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 MCPRegistrySearchResult: +class _MCPRegistrySearchResult: """Servers selected from the registry response.""" servers: Any @@ -8397,10 +9471,10 @@ class MCPRegistrySearchResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPRegistrySearchResult': + def from_dict(obj: Any) -> '_MCPRegistrySearchResult': assert isinstance(obj, dict) servers = obj.get("servers") - return MCPRegistrySearchResult(servers) + return _MCPRegistrySearchResult(servers) def to_dict(self) -> dict: result: dict = {} @@ -8425,7 +9499,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 @@ -8434,10 +9508,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 = {} @@ -8559,7 +9633,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" @@ -8602,7 +9676,7 @@ class OptionsUpdateToolFilterPrecedence(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 MCPShouldExcludeGitHubToolsResult: +class _MCPShouldExcludeGitHubToolsResult: """Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. """ @@ -8613,10 +9687,10 @@ class MCPShouldExcludeGitHubToolsResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPShouldExcludeGitHubToolsResult': + 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) + return _MCPShouldExcludeGitHubToolsResult(exclude_gh_replaceable_tools) def to_dict(self) -> dict: result: dict = {} @@ -8673,17 +9747,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 = {} @@ -9480,6 +10554,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 @@ -10576,6 +11736,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: @@ -10904,6 +12120,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: @@ -12616,9 +13892,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: @@ -12880,6 +14329,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: @@ -12910,6 +14455,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: @@ -12979,6 +14572,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: @@ -13009,6 +14654,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 @@ -13020,6 +14807,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: @@ -13698,6 +15542,7 @@ class SessionCapability(Enum): CANVAS_RENDERER = "canvas-renderer" CLI_DOCUMENTATION = "cli-documentation" ELICITATION = "elicitation" + GITHUB_CACHE_STORE = "github-cache-store" INTERACTIVE_MODE = "interactive-mode" MCP_APPS = "mcp-apps" MEMORY = "memory" @@ -16749,7 +18594,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': @@ -16807,6 +18656,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': @@ -16814,7 +18669,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 = {} @@ -16824,6 +18680,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. @@ -16953,6 +18811,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""" @@ -18129,6 +20009,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: @@ -18149,6 +20851,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: @@ -20195,6 +23041,86 @@ def to_dict(self) -> dict: result["source"] = to_enum(CatalogTrustSource, self.source) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CatalogAgentPluginMarketplaceSource: + """A plugin marketplace named by verified catalogue install data, installed as + `pluginName@name`. + """ + kind: ClassVar[str] = "marketplace" + """Discriminator: the plugin installs from a marketplace.""" + + name: str + """Marketplace name.""" + + repository: str + """Canonical lowercase GitHub owner/repository hosting the marketplace.""" + + @staticmethod + def from_dict(obj: Any) -> 'CatalogAgentPluginMarketplaceSource': + assert isinstance(obj, dict) + name = from_str(obj.get("name")) + repository = from_str(obj.get("repository")) + return CatalogAgentPluginMarketplaceSource(name, repository) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["name"] = from_str(self.name) + result["repository"] = from_str(self.repository) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CatalogAgentPluginMaterializationSource: + """Agent Finder's verified materialisation of the plugin. Present only when the candidate + carries a complete materialisation reference; the reference itself stays runtime-private. + """ + kind: ClassVar[str] = "materialization" + """Discriminator: the plugin installs from Agent Finder's materialisation.""" + + @staticmethod + def from_dict(obj: Any) -> 'CatalogAgentPluginMaterializationSource': + assert isinstance(obj, dict) + return CatalogAgentPluginMaterializationSource() + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CatalogAgentPluginRepositorySource: + """A GitHub repository holding the plugin, installed directly as `owner/repo[:path]` at the + plan's commit, without a marketplace. + """ + kind: ClassVar[str] = "repository" + """Discriminator: the plugin installs directly from a repository.""" + + repository: str + """Canonical lowercase GitHub owner/repository holding the plugin.""" + + path: str | None = None + """Relative POSIX path of the plugin directory in the repository. Omitted when the plugin is + at the repository root. + """ + + @staticmethod + def from_dict(obj: Any) -> 'CatalogAgentPluginRepositorySource': + assert isinstance(obj, dict) + repository = from_str(obj.get("repository")) + path = from_union([from_str, from_none], obj.get("path")) + return CatalogAgentPluginRepositorySource(repository, path) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["repository"] = from_str(self.repository) + if self.path is not None: + result["path"] = from_union([from_str, from_none], self.path) + return result + @dataclass class SourceClass: """Where the card came from: exactly one of a URL or embedded data, encoded as a tagged @@ -22227,6 +25153,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: @@ -23494,6 +26446,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 @@ -25129,6 +28135,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: @@ -25230,6 +28321,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: @@ -26181,7 +29766,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 MCPShouldExcludeGitHubToolsRequest: +class _MCPShouldExcludeGitHubToolsRequest: """The session tool filters that decide whether the session still has a shell to run `gh` with. """ @@ -26200,12 +29785,12 @@ class MCPShouldExcludeGitHubToolsRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPShouldExcludeGitHubToolsRequest': + 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) + return _MCPShouldExcludeGitHubToolsRequest(available_tools, excluded_tools, tool_filter_precedence) def to_dict(self) -> dict: result: dict = {} @@ -28191,6 +31776,31 @@ class CatalogAgentPluginCompatibilityTag(Enum): CANVAS_ONLY = "canvas-only" GITHUB_COPILOT = "github-copilot" +# Experimental: this type is part of an experimental API and may change or be removed. +class CatalogAgentPluginComponent(Enum): + """A kind of component an Agent Plugin contributes""" + + AGENT = "agent" + CANVAS = "canvas" + COMMAND = "command" + EXTENSION = "extension" + HOOK = "hook" + LSP_SERVER = "lsp-server" + MCP_SERVER = "mcp-server" + SKILL = "skill" + +# Experimental: this type is part of an experimental API and may change or be removed. +class CatalogAgentPluginInstallability(Enum): + """Whether this plugin can be installed in the selected session. Present only when the + caller requires the agent-plugin-confirmed-installation capability. + + Typed installability state for an Agent Plugin candidate + """ + FEATURE_DISABLED = "feature-disabled" + INSTALLABLE = "installable" + INSTALL_DATA_UNAVAILABLE = "install-data-unavailable" + NOT_INSTALLABLE_KIND = "not-installable-kind" + # Experimental: this type is part of an experimental API and may change or be removed. class CatalogAgentPluginCandidateKind(Enum): """Discriminator for an Agent Plugin candidate""" @@ -28199,12 +31809,39 @@ class CatalogAgentPluginCandidateKind(Enum): # Experimental: this type is part of an experimental API and may change or be removed. class CatalogAgentPluginMediaType(Enum): - """Canonical Agent Plugin media type. + """Media type naming the plugin's format. - Canonical Agent Plugin media type + Media type naming the plugin format of an Agent Plugin candidate. Values other than + `application/vnd.github.copilot-plugin` appear only when the caller requires the + agent-plugin-formats capability. """ + APPLICATION_AGENT_PLUGIN_JSON = "application/agent-plugin+json" + APPLICATION_VND_ANTHROPIC_CLAUDE_PLUGIN_JSON = "application/vnd.anthropic.claude-plugin+json" APPLICATION_VND_GITHUB_COPILOT_PLUGIN = "application/vnd.github.copilot-plugin" +class CatalogInstallability(Enum): + """Whether this MCP server can be planned for installation, and if policy prevents it. + + Whether an MCP server candidate can be planned for installation + + Whether this AI skill candidate can be planned for verified installation in the selected + session. + + Typed installability state for an AI skill candidate + + Whether this plugin can be installed in the selected session. Present only when the + caller requires the agent-plugin-confirmed-installation capability. + + Typed installability state for an Agent Plugin candidate + """ + FEATURE_DISABLED = "feature-disabled" + INSTALLABLE = "installable" + INSTALL_DATA_UNAVAILABLE = "install-data-unavailable" + MATERIALISATION_UNAVAILABLE = "materialisation-unavailable" + NOT_INSTALLABLE_KIND = "not-installable-kind" + NOT_INSTALLABLE_POLICY = "not-installable-policy" + POLICY_FORBIDS = "policy-forbids" + # Experimental: this type is part of an experimental API and may change or be removed. class CatalogCandidateKind(Enum): """Discriminator for an MCP server candidate @@ -28233,17 +31870,21 @@ class CatalogMediaType(Enum): Canonical AI skill media type - Canonical Agent Plugin media type. + Media type naming the plugin's format. - Canonical Agent Plugin media type + Media type naming the plugin format of an Agent Plugin candidate. Values other than + `application/vnd.github.copilot-plugin` appear only when the caller requires the + agent-plugin-formats capability. Media type the card was interpreted as, when it declared one this runtime recognises. Media type a catalog card is interpreted as """ + APPLICATION_AGENT_PLUGIN_JSON = "application/agent-plugin+json" APPLICATION_AI_SKILL = "application/ai-skill" APPLICATION_MCP_SERVER_CARD_JSON = "application/mcp-server-card+json" APPLICATION_MCP_SERVER_JSON = "application/mcp-server+json" + APPLICATION_VND_ANTHROPIC_CLAUDE_PLUGIN_JSON = "application/vnd.anthropic.claude-plugin+json" APPLICATION_VND_GITHUB_COPILOT_PLUGIN = "application/vnd.github.copilot-plugin" # Experimental: this type is part of an experimental API and may change or be removed. @@ -30042,6 +33683,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: @@ -30206,6 +33932,37 @@ def to_dict(self) -> 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: @@ -31430,6 +35187,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. @@ -31479,6 +35239,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")) @@ -31490,7 +35251,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 = {} @@ -31500,6 +35261,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: @@ -31545,6 +35308,9 @@ class CatalogClientContract: Required authenticated bound catalogue and Skill installation capabilities. Required authenticated bound Skill installation capability. + + Required authenticated bound catalogue, skill-confirmed-installation and + skill-reference-installation capabilities. """ protocol_version: int """SDK protocol version the caller was generated against. A caller below the runtime's @@ -31571,6 +35337,53 @@ def to_dict(self) -> dict: result["requiredCapabilities"] = from_list(from_str, self.required_capabilities) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CatalogMaterializationReference: + """An Agent Finder materialisation reference pinning one exact resource revision. + + It carries no origin or URL: the runtime always builds the descriptor URL on its fixed + Agent + Finder authority. It carries no display data either; every reviewed field comes from the + verified descriptor and Skill files. + + Materialisation reference to verify and plan. + """ + bundle_digest: str + """Expected `sha256:` digest of the descriptor's bundle manifest.""" + + descriptor_digest: str + """Expected `sha256:` digest of the canonical descriptor.""" + + resource_id: str + """Agent Finder resource identifier, a positive decimal integer.""" + + revision_id: str + """Agent Finder materialisation revision identifier, a positive decimal integer. This is not + a Git commit. + """ + schema_version: str + """Materialisation descriptor schema version, currently `1.0.0`.""" + + @staticmethod + def from_dict(obj: Any) -> 'CatalogMaterializationReference': + assert isinstance(obj, dict) + bundle_digest = from_str(obj.get("bundleDigest")) + descriptor_digest = from_str(obj.get("descriptorDigest")) + resource_id = from_str(obj.get("resourceId")) + revision_id = from_str(obj.get("revisionId")) + schema_version = from_str(obj.get("schemaVersion")) + return CatalogMaterializationReference(bundle_digest, descriptor_digest, resource_id, revision_id, schema_version) + + def to_dict(self) -> dict: + result: dict = {} + result["bundleDigest"] = from_str(self.bundle_digest) + result["descriptorDigest"] = from_str(self.descriptor_digest) + result["resourceId"] = from_str(self.resource_id) + result["revisionId"] = from_str(self.revision_id) + result["schemaVersion"] = from_str(self.schema_version) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SkillInstallationFileReview: @@ -31733,12 +35546,16 @@ class SkillInstallationFailureReason(Enum): CONFIRMATION_INVALID = "confirmation-invalid" CONFIRMATION_UNAVAILABLE = "confirmation-unavailable" DESCRIPTOR_INVALID = "descriptor-invalid" + DESCRIPTOR_NOT_FOUND = "descriptor-not-found" DESCRIPTOR_UNAVAILABLE = "descriptor-unavailable" + DESCRIPTOR_WITHDRAWN = "descriptor-withdrawn" + DIGEST_MISMATCH = "digest-mismatch" ENTRYPOINT_UNAVAILABLE = "entrypoint-unavailable" EXPIRED = "expired" FEATURE_DISABLED = "feature-disabled" FOREIGN_RUNTIME = "foreign-runtime" INVALID_CANDIDATE = "invalid-candidate" + INVALID_REFERENCE = "invalid-reference" INVALID_REQUEST = "invalid-request" INVALID_SKILL = "invalid-skill" LIFECYCLE_UNAVAILABLE = "lifecycle-unavailable" @@ -31754,6 +35571,7 @@ class SkillInstallationFailureReason(Enum): REVIEW_TOO_LARGE = "review-too-large" SEARCH_MISMATCH = "search-mismatch" SOURCE_CHANGED = "source-changed" + UNSUPPORTED_RESOURCE = "unsupported-resource" WRITE_FAILED = "write-failed" WRONG_KIND = "wrong-kind" @@ -32099,6 +35917,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: @@ -33024,6 +36905,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: @@ -35731,6 +39857,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: @@ -36792,6 +40938,185 @@ def to_dict(self) -> dict: 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: @@ -37551,6 +41876,78 @@ def to_dict(self) -> dict: result["scope"] = to_enum(AdditionalContentExclusionPolicyScope, self.scope) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CatalogAgentPluginInstallPlan: + """Reviewable install plan built only from verified catalogue install data. Present exactly + when installability is `installable`; the runtime never derives it from the repository + source, display text, or identity. + + What installing an Agent Plugin would do, for the user to review and confirm. + Provisional: the catalogue install data it is built from is not final. + """ + canvas_ids: list[str] + """Canvas IDs the plugin contributes. Non-empty exactly when components include `canvas`.""" + + components: list[CatalogAgentPluginComponent] + """Components the plugin contributes, in canonical order.""" + + connector_dependencies: list[str] + """Connectors the plugin depends on.""" + + extension_ids: list[str] + """Extension IDs the plugin contributes. Non-empty exactly when components include + `extension`. + """ + mcp_dependencies: list[str] + """MCP servers the plugin depends on but does not bundle.""" + + plugin_name: str + """The plugin's name, as named in its marketplace or manifest.""" + + sources: list[CatalogAgentPluginInstallSource] + """Every valid install source, in preference order: materialization, then marketplace, then + repository. Each kind appears at most once. + """ + commit: str | None = None + """Exact full commit ID to install, in lowercase hex. Always present when the plan has a + repository source; a plan with a marketplace source carries at least one of version and + commit. + """ + version: str | None = None + """Exact plugin version to install. A plan with a marketplace source carries at least one of + version and commit. + """ + + @staticmethod + def from_dict(obj: Any) -> 'CatalogAgentPluginInstallPlan': + assert isinstance(obj, dict) + canvas_ids = from_list(from_str, obj.get("canvasIds")) + components = from_list(CatalogAgentPluginComponent, obj.get("components")) + connector_dependencies = from_list(from_str, obj.get("connectorDependencies")) + extension_ids = from_list(from_str, obj.get("extensionIds")) + mcp_dependencies = from_list(from_str, obj.get("mcpDependencies")) + plugin_name = from_str(obj.get("pluginName")) + sources = from_list(_load_CatalogAgentPluginInstallSource, obj.get("sources")) + commit = from_union([from_str, from_none], obj.get("commit")) + version = from_union([from_str, from_none], obj.get("version")) + return CatalogAgentPluginInstallPlan(canvas_ids, components, connector_dependencies, extension_ids, mcp_dependencies, plugin_name, sources, commit, version) + + def to_dict(self) -> dict: + result: dict = {} + result["canvasIds"] = from_list(from_str, self.canvas_ids) + result["components"] = from_list(lambda x: to_enum(CatalogAgentPluginComponent, x), self.components) + result["connectorDependencies"] = from_list(from_str, self.connector_dependencies) + result["extensionIds"] = from_list(from_str, self.extension_ids) + result["mcpDependencies"] = from_list(from_str, self.mcp_dependencies) + result["pluginName"] = from_str(self.plugin_name) + result["sources"] = from_list(lambda x: (x).to_dict(), self.sources) + if self.commit is not None: + result["commit"] = from_union([from_str, from_none], self.commit) + if self.version is not None: + 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 CatalogAgentPluginCandidateProvenance: @@ -37565,7 +41962,7 @@ class CatalogAgentPluginCandidateProvenance: credentials. Inert untrusted data. """ media_type: CatalogAgentPluginMediaType - """Canonical Agent Plugin media type.""" + """Media type naming the plugin's format.""" observed_at: str """ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a @@ -37648,7 +42045,7 @@ class CatalogCandidateProvenance: Media type advertised for the referenced AI skill card - Canonical Agent Plugin media type. + Media type naming the plugin's format. """ observed_at: str """ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a @@ -39453,6 +43850,37 @@ def to_dict(self) -> dict: result["policySessionId"] = from_str(self.policy_session_id) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SkillPlanInstallFromReferenceRequest: + """Side-effect-free planning of one Skill from a materialisation reference rather than a + search candidate. + """ + contract: CatalogClientContract + """Required authenticated bound catalogue, skill-confirmed-installation and + skill-reference-installation capabilities. + """ + policy_session_id: str + """Existing local session attached to this connection.""" + + reference: CatalogMaterializationReference + """Materialisation reference to verify and plan.""" + + @staticmethod + def from_dict(obj: Any) -> 'SkillPlanInstallFromReferenceRequest': + assert isinstance(obj, dict) + contract = CatalogClientContract.from_dict(obj.get("contract")) + policy_session_id = from_str(obj.get("policySessionId")) + reference = CatalogMaterializationReference.from_dict(obj.get("reference")) + return SkillPlanInstallFromReferenceRequest(contract, policy_session_id, reference) + + def to_dict(self) -> dict: + result: dict = {} + result["contract"] = to_class(CatalogClientContract, self.contract) + result["policySessionId"] = from_str(self.policy_session_id) + result["reference"] = to_class(CatalogMaterializationReference, self.reference) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SkillInstallationSummary: @@ -40037,6 +44465,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: @@ -42790,7 +47238,7 @@ class CatalogAgentPluginCandidate: """Discriminator: this candidate describes an Agent Plugin.""" media_type: CatalogAgentPluginMediaType - """Canonical Agent Plugin media type.""" + """Media type naming the plugin's format.""" provenance: CatalogAgentPluginCandidateProvenance """Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw @@ -42803,6 +47251,15 @@ class CatalogAgentPluginCandidate: description: str | None = None """Description taken verbatim from the card. Inert untrusted text.""" + installability: CatalogAgentPluginInstallability | None = None + """Whether this plugin can be installed in the selected session. Present only when the + caller requires the agent-plugin-confirmed-installation capability. + """ + install_plan: CatalogAgentPluginInstallPlan | None = None + """Reviewable install plan built only from verified catalogue install data. Present exactly + when installability is `installable`; the runtime never derives it from the repository + source, display text, or identity. + """ publisher: str | None = None """Publisher taken verbatim from the card. Inert untrusted text.""" @@ -42827,10 +47284,12 @@ def from_dict(obj: Any) -> 'CatalogAgentPluginCandidate': provenance = CatalogAgentPluginCandidateProvenance.from_dict(obj.get("provenance")) source = CatalogPluginRepositorySource.from_dict(obj.get("source")) description = from_union([from_str, from_none], obj.get("description")) + installability = from_union([CatalogAgentPluginInstallability, from_none], obj.get("installability")) + install_plan = from_union([CatalogAgentPluginInstallPlan.from_dict, from_none], obj.get("installPlan")) publisher = from_union([from_str, from_none], obj.get("publisher")) trust = from_union([_load_CatalogTrustSnapshot, from_none], obj.get("trust")) version = from_union([from_str, from_none], obj.get("version")) - return CatalogAgentPluginCandidate(compatibility_tags, display_name, identity, kind, media_type, provenance, source, description, publisher, trust, version) + return CatalogAgentPluginCandidate(compatibility_tags, display_name, identity, kind, media_type, provenance, source, description, installability, install_plan, publisher, trust, version) def to_dict(self) -> dict: result: dict = {} @@ -42843,6 +47302,10 @@ def to_dict(self) -> dict: result["source"] = to_class(CatalogPluginRepositorySource, self.source) if self.description is not None: result["description"] = from_union([from_str, from_none], self.description) + if self.installability is not None: + result["installability"] = from_union([lambda x: to_enum(CatalogAgentPluginInstallability, x), from_none], self.installability) + if self.install_plan is not None: + result["installPlan"] = from_union([lambda x: to_class(CatalogAgentPluginInstallPlan, x), from_none], self.install_plan) if self.publisher is not None: result["publisher"] = from_union([from_str, from_none], self.publisher) if self.trust is not None: @@ -42886,7 +47349,7 @@ class CatalogCandidate: Media type of the underlying AI skill card - Canonical Agent Plugin media type. + Media type naming the plugin's format. """ provenance: CatalogCandidateProvenance """Where the catalog reference was observed, without the card itself or any content digest. @@ -42917,6 +47380,9 @@ class CatalogCandidate: Whether this AI skill candidate can be planned for verified installation in the selected session. + + Whether this plugin can be installed in the selected session. Present only when the + caller requires the agent-plugin-confirmed-installation capability. """ publisher: str | None = None """Publisher taken verbatim from the card. Inert untrusted text.""" @@ -42939,6 +47405,11 @@ class CatalogCandidate: """Validated, normalised catalogue resource URN. This identity comes only from the catalog identifier and is never inferred from display text or installed-plugin state. """ + install_plan: CatalogAgentPluginInstallPlan | None = None + """Reviewable install plan built only from verified catalogue install data. Present exactly + when installability is `installable`; the runtime never derives it from the repository + source, display text, or identity. + """ version: str | None = None """Optional version declared by the catalog source. Omitted rather than guessed when the source supplies no version. @@ -42960,8 +47431,9 @@ def from_dict(obj: Any) -> 'CatalogCandidate': trust = from_union([_load_CatalogTrustSnapshot, from_none], obj.get("trust")) compatibility_tags = from_union([lambda x: from_list(CatalogAgentPluginCompatibilityTag, x), from_none], obj.get("compatibilityTags")) identity = from_union([from_str, from_none], obj.get("identity")) + install_plan = from_union([CatalogAgentPluginInstallPlan.from_dict, from_none], obj.get("installPlan")) version = from_union([from_str, from_none], obj.get("version")) - return CatalogCandidate(display_name, kind, media_type, provenance, source, description, handle, handle_expires_at, installability, publisher, trust, compatibility_tags, identity, version) + return CatalogCandidate(display_name, kind, media_type, provenance, source, description, handle, handle_expires_at, installability, publisher, trust, compatibility_tags, identity, install_plan, version) def to_dict(self) -> dict: result: dict = {} @@ -42986,6 +47458,8 @@ def to_dict(self) -> dict: result["compatibilityTags"] = from_union([lambda x: from_list(lambda x: to_enum(CatalogAgentPluginCompatibilityTag, x), x), from_none], self.compatibility_tags) if self.identity is not None: result["identity"] = from_union([from_str, from_none], self.identity) + if self.install_plan is not None: + result["installPlan"] = from_union([lambda x: to_class(CatalogAgentPluginInstallPlan, x), from_none], self.install_plan) if self.version is not None: result["version"] = from_union([from_str, from_none], self.version) return result @@ -44273,6 +48747,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. @@ -44313,8 +48794,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 @@ -44335,9 +48817,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.""" @@ -44364,6 +48847,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. @@ -44382,6 +48873,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.""" @@ -44455,8 +48953,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.""" @@ -44486,6 +48988,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")) @@ -44512,10 +49015,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")) @@ -44544,7 +49049,7 @@ def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': 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 = {} @@ -44552,6 +49057,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: @@ -44604,6 +49111,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: @@ -44612,6 +49121,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: @@ -45555,6 +50066,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: @@ -45813,59 +50452,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: @@ -46783,7 +51369,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 @@ -46803,13 +51389,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 = {} @@ -46822,7 +51408,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 MCPRegistrySearchRequest: +class _MCPRegistrySearchRequest: """Registry search terms, the credential to search under, and the request id that makes the search cancellable. """ @@ -46850,14 +51436,14 @@ class MCPRegistrySearchRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPRegistrySearchRequest': + 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) + return _MCPRegistrySearchRequest(auth_info, limit, request_id, query, repository) def to_dict(self) -> dict: result: dict = {} @@ -47119,7 +51705,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] @@ -47139,7 +51725,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")) @@ -47154,7 +51740,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 = {} @@ -47423,13 +52009,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.""" @@ -47493,10 +52079,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")) @@ -47517,12 +52103,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: @@ -48534,6 +53120,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. @@ -48634,8 +53227,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.""" @@ -48680,6 +53273,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. @@ -48697,9 +53298,23 @@ 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.""" + interactive_user: bool | None = None + """Declares that a human drives this session's conversation, enabling user-message analysis + (sentiment, task completion criteria, and task analytics) for user-authored messages. + Automated messages remain excluded. Defaults to false for SDK sessions. Applied when the + session is created or loaded from storage; ignored when resuming a session that is + already active, and for cloud sessions. + """ is_experimental_mode: bool | None = None """Whether experimental behavior is enabled.""" @@ -48797,8 +53412,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.""" @@ -48820,6 +53439,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")) @@ -48855,11 +53475,14 @@ 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")) + interactive_user = from_union([from_bool, from_none], obj.get("interactiveUser")) is_experimental_mode = from_union([from_bool, from_none], obj.get("isExperimentalMode")) log_interactive_shells = from_union([from_bool, from_none], obj.get("logInteractiveShells")) lsp_client_name = from_union([from_str, from_none], obj.get("lspClientName")) @@ -48894,7 +53517,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, interactive_user, 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 = {} @@ -48904,6 +53527,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: @@ -48974,6 +53599,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: @@ -48982,8 +53609,12 @@ 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.interactive_user is not None: + result["interactiveUser"] = from_union([from_bool, from_none], self.interactive_user) if self.is_experimental_mode is not None: result["isExperimentalMode"] = from_union([from_bool, from_none], self.is_experimental_mode) if self.log_interactive_shells is not None: @@ -49497,6 +54128,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: @@ -50167,10 +54832,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 @@ -50286,7 +54958,17 @@ class RPC: catalog_agent_plugin_candidate_kind: CatalogAgentPluginCandidateKind catalog_agent_plugin_candidate_provenance: CatalogAgentPluginCandidateProvenance catalog_agent_plugin_compatibility_tag: CatalogAgentPluginCompatibilityTag + catalog_agent_plugin_component: CatalogAgentPluginComponent + catalog_agent_plugin_installability: CatalogAgentPluginInstallability + catalog_agent_plugin_install_plan: CatalogAgentPluginInstallPlan + catalog_agent_plugin_install_source: CatalogAgentPluginInstallSource + catalog_agent_plugin_marketplace_source: CatalogAgentPluginMarketplaceSource + catalog_agent_plugin_marketplace_source_kind: CatalogAgentPluginMarketplaceSourceKind + catalog_agent_plugin_materialization_source: CatalogAgentPluginMaterializationSource + catalog_agent_plugin_materialization_source_kind: CatalogAgentPluginMaterializationSourceKind catalog_agent_plugin_media_type: CatalogAgentPluginMediaType + catalog_agent_plugin_repository_source: CatalogAgentPluginRepositorySource + catalog_agent_plugin_repository_source_kind: CatalogAgentPluginRepositorySourceKind catalog_ai_skill_candidate: CatalogAISkillCandidate catalog_ai_skill_candidate_kind: CatalogAISkillCandidateKind catalog_ai_skill_candidate_provenance: CatalogAISkillCandidateProvenance @@ -50311,6 +54993,7 @@ class RPC: catalog_invalid_request_field: CatalogInvalidRequestField catalog_malformed_card_error: CatalogMalformedCardError catalog_malformed_card_reason: CatalogMalformedCardReason + catalog_materialization_reference: CatalogMaterializationReference catalog_mcp_server_candidate: CatalogMCPServerCandidate catalog_mcp_server_candidate_kind: CatalogMCPServerCandidateKind catalog_mcp_server_candidate_provenance: CatalogMCPServerCandidateProvenance @@ -50426,6 +55109,8 @@ class RPC: content_exclusion_check_paths_request: ContentExclusionCheckPathsRequest content_exclusion_check_paths_result: ContentExclusionCheckPathsResult content_exclusion_path_check: ContentExclusionPathCheck + content_exclusion_validate_policies_request: ContentExclusionValidatePoliciesRequest + content_exclusion_validate_policies_result: ContentExclusionValidatePoliciesResult content_filter_mode: ContentFilterMode context_heaviest_message: ContextHeaviestMessage copilot_api_token_auth_info: CopilotAPITokenAuthInfo @@ -50521,12 +55206,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 @@ -50536,7 +55230,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 @@ -50622,9 +55316,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 @@ -50644,6 +55340,7 @@ 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 @@ -50659,19 +55356,44 @@ class RPC: 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 @@ -50719,7 +55441,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 @@ -50757,6 +55479,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 @@ -50823,14 +55546,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_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_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 @@ -50863,8 +55586,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 @@ -50874,8 +55597,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_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 @@ -50887,7 +55610,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 @@ -50923,6 +55646,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 @@ -51058,6 +55783,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 @@ -51094,6 +55821,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 @@ -51238,7 +55967,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 @@ -51254,18 +55993,40 @@ class RPC: 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 @@ -51494,6 +56255,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 @@ -51531,6 +56295,7 @@ class RPC: skill_installation_summary: SkillInstallationSummary skill_install_plan: SkillInstallPlan skill_list: SkillList + skill_plan_install_from_reference_request: SkillPlanInstallFromReferenceRequest skill_plan_install_request: SkillPlanInstallRequest skill_plan_uninstall_request: SkillPlanUninstallRequest skill_provider_descriptor: SkillProviderDescriptor @@ -51566,6 +56331,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 @@ -51680,8 +56447,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 @@ -51782,10 +56596,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")) @@ -51901,7 +56722,17 @@ def from_dict(obj: Any) -> 'RPC': catalog_agent_plugin_candidate_kind = CatalogAgentPluginCandidateKind(obj.get("CatalogAgentPluginCandidateKind")) catalog_agent_plugin_candidate_provenance = CatalogAgentPluginCandidateProvenance.from_dict(obj.get("CatalogAgentPluginCandidateProvenance")) catalog_agent_plugin_compatibility_tag = CatalogAgentPluginCompatibilityTag(obj.get("CatalogAgentPluginCompatibilityTag")) + catalog_agent_plugin_component = CatalogAgentPluginComponent(obj.get("CatalogAgentPluginComponent")) + catalog_agent_plugin_installability = CatalogAgentPluginInstallability(obj.get("CatalogAgentPluginInstallability")) + catalog_agent_plugin_install_plan = CatalogAgentPluginInstallPlan.from_dict(obj.get("CatalogAgentPluginInstallPlan")) + catalog_agent_plugin_install_source = _load_CatalogAgentPluginInstallSource(obj.get("CatalogAgentPluginInstallSource")) + catalog_agent_plugin_marketplace_source = CatalogAgentPluginMarketplaceSource.from_dict(obj.get("CatalogAgentPluginMarketplaceSource")) + catalog_agent_plugin_marketplace_source_kind = CatalogAgentPluginMarketplaceSourceKind(obj.get("CatalogAgentPluginMarketplaceSourceKind")) + catalog_agent_plugin_materialization_source = CatalogAgentPluginMaterializationSource.from_dict(obj.get("CatalogAgentPluginMaterializationSource")) + catalog_agent_plugin_materialization_source_kind = CatalogAgentPluginMaterializationSourceKind(obj.get("CatalogAgentPluginMaterializationSourceKind")) catalog_agent_plugin_media_type = CatalogAgentPluginMediaType(obj.get("CatalogAgentPluginMediaType")) + catalog_agent_plugin_repository_source = CatalogAgentPluginRepositorySource.from_dict(obj.get("CatalogAgentPluginRepositorySource")) + catalog_agent_plugin_repository_source_kind = CatalogAgentPluginRepositorySourceKind(obj.get("CatalogAgentPluginRepositorySourceKind")) catalog_ai_skill_candidate = CatalogAISkillCandidate.from_dict(obj.get("CatalogAiSkillCandidate")) catalog_ai_skill_candidate_kind = CatalogAISkillCandidateKind(obj.get("CatalogAiSkillCandidateKind")) catalog_ai_skill_candidate_provenance = CatalogAISkillCandidateProvenance.from_dict(obj.get("CatalogAiSkillCandidateProvenance")) @@ -51926,6 +56757,7 @@ def from_dict(obj: Any) -> 'RPC': catalog_invalid_request_field = CatalogInvalidRequestField(obj.get("CatalogInvalidRequestField")) catalog_malformed_card_error = CatalogMalformedCardError.from_dict(obj.get("CatalogMalformedCardError")) catalog_malformed_card_reason = CatalogMalformedCardReason(obj.get("CatalogMalformedCardReason")) + catalog_materialization_reference = CatalogMaterializationReference.from_dict(obj.get("CatalogMaterializationReference")) catalog_mcp_server_candidate = CatalogMCPServerCandidate.from_dict(obj.get("CatalogMcpServerCandidate")) catalog_mcp_server_candidate_kind = CatalogMCPServerCandidateKind(obj.get("CatalogMcpServerCandidateKind")) catalog_mcp_server_candidate_provenance = CatalogMCPServerCandidateProvenance.from_dict(obj.get("CatalogMcpServerCandidateProvenance")) @@ -52041,6 +56873,8 @@ def from_dict(obj: Any) -> 'RPC': content_exclusion_check_paths_request = ContentExclusionCheckPathsRequest.from_dict(obj.get("ContentExclusionCheckPathsRequest")) content_exclusion_check_paths_result = ContentExclusionCheckPathsResult.from_dict(obj.get("ContentExclusionCheckPathsResult")) content_exclusion_path_check = ContentExclusionPathCheck.from_dict(obj.get("ContentExclusionPathCheck")) + content_exclusion_validate_policies_request = ContentExclusionValidatePoliciesRequest.from_dict(obj.get("ContentExclusionValidatePoliciesRequest")) + content_exclusion_validate_policies_result = ContentExclusionValidatePoliciesResult.from_dict(obj.get("ContentExclusionValidatePoliciesResult")) content_filter_mode = ContentFilterMode(obj.get("ContentFilterMode")) context_heaviest_message = ContextHeaviestMessage.from_dict(obj.get("ContextHeaviestMessage")) copilot_api_token_auth_info = CopilotAPITokenAuthInfo.from_dict(obj.get("CopilotApiTokenAuthInfo")) @@ -52136,12 +56970,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")) @@ -52151,7 +56994,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")) @@ -52237,9 +57080,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")) @@ -52259,6 +57104,7 @@ 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")) @@ -52274,19 +57120,44 @@ def from_dict(obj: Any) -> 'RPC': 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")) @@ -52334,7 +57205,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")) @@ -52372,6 +57243,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")) @@ -52438,14 +57310,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_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_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")) @@ -52478,8 +57350,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")) @@ -52489,8 +57361,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_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")) @@ -52502,7 +57374,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")) @@ -52538,6 +57410,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")) @@ -52673,6 +57547,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")) @@ -52709,6 +57585,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")) @@ -52853,7 +57731,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")) @@ -52869,18 +57757,40 @@ def from_dict(obj: Any) -> 'RPC': 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")) @@ -53109,6 +58019,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")) @@ -53146,6 +58059,7 @@ def from_dict(obj: Any) -> 'RPC': skill_installation_summary = SkillInstallationSummary.from_dict(obj.get("SkillInstallationSummary")) skill_install_plan = SkillInstallPlan.from_dict(obj.get("SkillInstallPlan")) skill_list = SkillList.from_dict(obj.get("SkillList")) + skill_plan_install_from_reference_request = SkillPlanInstallFromReferenceRequest.from_dict(obj.get("SkillPlanInstallFromReferenceRequest")) skill_plan_install_request = SkillPlanInstallRequest.from_dict(obj.get("SkillPlanInstallRequest")) skill_plan_uninstall_request = SkillPlanUninstallRequest.from_dict(obj.get("SkillPlanUninstallRequest")) skill_provider_descriptor = SkillProviderDescriptor.from_dict(obj.get("SkillProviderDescriptor")) @@ -53181,6 +58095,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")) @@ -53295,8 +58211,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")) @@ -53388,7 +58351,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_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_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_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_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_credential_suggestion, sandbox_credential_suggestions_result, 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_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, 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_component, catalog_agent_plugin_installability, catalog_agent_plugin_install_plan, catalog_agent_plugin_install_source, catalog_agent_plugin_marketplace_source, catalog_agent_plugin_marketplace_source_kind, catalog_agent_plugin_materialization_source, catalog_agent_plugin_materialization_source_kind, catalog_agent_plugin_media_type, catalog_agent_plugin_repository_source, catalog_agent_plugin_repository_source_kind, 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_materialization_reference, 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_exclusion_validate_policies_request, content_exclusion_validate_policies_result, 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_from_reference_request, 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 = {} @@ -53397,10 +58360,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) @@ -53516,7 +58486,17 @@ def to_dict(self) -> dict: result["CatalogAgentPluginCandidateKind"] = to_enum(CatalogAgentPluginCandidateKind, self.catalog_agent_plugin_candidate_kind) result["CatalogAgentPluginCandidateProvenance"] = to_class(CatalogAgentPluginCandidateProvenance, self.catalog_agent_plugin_candidate_provenance) result["CatalogAgentPluginCompatibilityTag"] = to_enum(CatalogAgentPluginCompatibilityTag, self.catalog_agent_plugin_compatibility_tag) + result["CatalogAgentPluginComponent"] = to_enum(CatalogAgentPluginComponent, self.catalog_agent_plugin_component) + result["CatalogAgentPluginInstallability"] = to_enum(CatalogAgentPluginInstallability, self.catalog_agent_plugin_installability) + result["CatalogAgentPluginInstallPlan"] = to_class(CatalogAgentPluginInstallPlan, self.catalog_agent_plugin_install_plan) + result["CatalogAgentPluginInstallSource"] = (self.catalog_agent_plugin_install_source).to_dict() + result["CatalogAgentPluginMarketplaceSource"] = to_class(CatalogAgentPluginMarketplaceSource, self.catalog_agent_plugin_marketplace_source) + result["CatalogAgentPluginMarketplaceSourceKind"] = to_enum(CatalogAgentPluginMarketplaceSourceKind, self.catalog_agent_plugin_marketplace_source_kind) + result["CatalogAgentPluginMaterializationSource"] = to_class(CatalogAgentPluginMaterializationSource, self.catalog_agent_plugin_materialization_source) + result["CatalogAgentPluginMaterializationSourceKind"] = to_enum(CatalogAgentPluginMaterializationSourceKind, self.catalog_agent_plugin_materialization_source_kind) result["CatalogAgentPluginMediaType"] = to_enum(CatalogAgentPluginMediaType, self.catalog_agent_plugin_media_type) + result["CatalogAgentPluginRepositorySource"] = to_class(CatalogAgentPluginRepositorySource, self.catalog_agent_plugin_repository_source) + result["CatalogAgentPluginRepositorySourceKind"] = to_enum(CatalogAgentPluginRepositorySourceKind, self.catalog_agent_plugin_repository_source_kind) result["CatalogAiSkillCandidate"] = to_class(CatalogAISkillCandidate, self.catalog_ai_skill_candidate) result["CatalogAiSkillCandidateKind"] = to_enum(CatalogAISkillCandidateKind, self.catalog_ai_skill_candidate_kind) result["CatalogAiSkillCandidateProvenance"] = to_class(CatalogAISkillCandidateProvenance, self.catalog_ai_skill_candidate_provenance) @@ -53541,6 +58521,7 @@ def to_dict(self) -> dict: result["CatalogInvalidRequestField"] = to_enum(CatalogInvalidRequestField, self.catalog_invalid_request_field) result["CatalogMalformedCardError"] = to_class(CatalogMalformedCardError, self.catalog_malformed_card_error) result["CatalogMalformedCardReason"] = to_enum(CatalogMalformedCardReason, self.catalog_malformed_card_reason) + result["CatalogMaterializationReference"] = to_class(CatalogMaterializationReference, self.catalog_materialization_reference) result["CatalogMcpServerCandidate"] = to_class(CatalogMCPServerCandidate, self.catalog_mcp_server_candidate) result["CatalogMcpServerCandidateKind"] = to_enum(CatalogMCPServerCandidateKind, self.catalog_mcp_server_candidate_kind) result["CatalogMcpServerCandidateProvenance"] = to_class(CatalogMCPServerCandidateProvenance, self.catalog_mcp_server_candidate_provenance) @@ -53656,6 +58637,8 @@ def to_dict(self) -> dict: result["ContentExclusionCheckPathsRequest"] = to_class(ContentExclusionCheckPathsRequest, self.content_exclusion_check_paths_request) result["ContentExclusionCheckPathsResult"] = to_class(ContentExclusionCheckPathsResult, self.content_exclusion_check_paths_result) result["ContentExclusionPathCheck"] = to_class(ContentExclusionPathCheck, self.content_exclusion_path_check) + result["ContentExclusionValidatePoliciesRequest"] = to_class(ContentExclusionValidatePoliciesRequest, self.content_exclusion_validate_policies_request) + result["ContentExclusionValidatePoliciesResult"] = to_class(ContentExclusionValidatePoliciesResult, self.content_exclusion_validate_policies_result) result["ContentFilterMode"] = to_enum(ContentFilterMode, self.content_filter_mode) result["ContextHeaviestMessage"] = to_class(ContextHeaviestMessage, self.context_heaviest_message) result["CopilotApiTokenAuthInfo"] = to_class(CopilotAPITokenAuthInfo, self.copilot_api_token_auth_info) @@ -53751,12 +58734,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) @@ -53766,7 +58758,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) @@ -53852,9 +58844,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) @@ -53874,6 +58868,7 @@ 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) @@ -53889,19 +58884,44 @@ def to_dict(self) -> dict: 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) @@ -53949,7 +58969,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) @@ -53987,6 +59007,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) @@ -54053,14 +59074,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["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["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) @@ -54093,8 +59114,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) @@ -54104,8 +59125,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["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) @@ -54117,7 +59138,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) @@ -54153,6 +59174,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) @@ -54288,6 +59311,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) @@ -54324,6 +59349,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) @@ -54468,7 +59495,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) @@ -54484,18 +59521,40 @@ def to_dict(self) -> dict: 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) @@ -54724,6 +59783,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) @@ -54761,6 +59823,7 @@ def to_dict(self) -> dict: result["SkillInstallationSummary"] = to_class(SkillInstallationSummary, self.skill_installation_summary) result["SkillInstallPlan"] = to_class(SkillInstallPlan, self.skill_install_plan) result["SkillList"] = to_class(SkillList, self.skill_list) + result["SkillPlanInstallFromReferenceRequest"] = to_class(SkillPlanInstallFromReferenceRequest, self.skill_plan_install_from_reference_request) result["SkillPlanInstallRequest"] = to_class(SkillPlanInstallRequest, self.skill_plan_install_request) result["SkillPlanUninstallRequest"] = to_class(SkillPlanUninstallRequest, self.skill_plan_uninstall_request) result["SkillProviderDescriptor"] = to_class(SkillProviderDescriptor, self.skill_provider_descriptor) @@ -54796,6 +59859,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) @@ -54910,8 +59975,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) @@ -55041,6 +60153,18 @@ def _load_AuthInfo(obj: Any) -> "AuthInfo": case "api-key": return APIKeyAuthInfo.from_dict(obj) case _: raise ValueError(f"Unknown AuthInfo type: {kind!r}") +# One place an Agent Plugin can install from: Agent Finder's materialisation, a marketplace, or a GitHub repository. Each variant carries only its own fields. +CatalogAgentPluginInstallSource = CatalogAgentPluginMaterializationSource | CatalogAgentPluginMarketplaceSource | CatalogAgentPluginRepositorySource + +def _load_CatalogAgentPluginInstallSource(obj: Any) -> "CatalogAgentPluginInstallSource": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "materialization": return CatalogAgentPluginMaterializationSource.from_dict(obj) + case "marketplace": return CatalogAgentPluginMarketplaceSource.from_dict(obj) + case "repository": return CatalogAgentPluginRepositorySource.from_dict(obj) + case _: raise ValueError(f"Unknown CatalogAgentPluginInstallSource kind: {kind!r}") + # 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. CatalogCandidateSource = CatalogCandidateSourceURL | CatalogCandidateSourceEmbedded @@ -55426,6 +60550,8 @@ def _load_TaskInfo(obj: Any) -> "TaskInfo": EventsCursorStatus = CursorStatus ExternalToolResult = ExternalToolTextResultForLlm ExternalToolTextResultForLlmContentResourceLinkIconTheme = Theme +FeatureFlagsResolveResult = dict +FeatureFlagsResolveStartupGatesResult = dict FilterMapping = dict HistoryCompactRequest = Any InstructionDiscoveryPathKind = DebugCollectLogsEntryKind @@ -55471,7 +60597,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 @@ -55491,6 +60625,8 @@ def _load_TaskInfo(obj: Any) -> "TaskInfo": TaskInfoExecutionMode = TaskExecutionMode TaskInfoStatus = TaskStatus ToolResult = ToolResultExpanded +UserSettingsBuildRuntimeResult = dict +UserSettingsLoadForConfigDirResult = dict WorkspaceSummaryHostType = HostType WorkspacesWorkspaceDetailsHostType = HostType @@ -55522,6 +60658,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"): @@ -55596,44 +60849,14 @@ async def get_built_in_catalog(self, *, timeout: float | None = None) -> BuiltIn # Experimental: this API group is experimental and may change or be removed. -class ServerSandboxProxyCaApi: +class ServerContentExclusionApi: 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." + async def validate_policies(self, params: ContentExclusionValidatePoliciesRequest, *, timeout: float | None = None) -> ContentExclusionValidatePoliciesResult: + "Validates candidate content-exclusion policies and returns them normalized, without starting a session. This runs the same parser the runtime's own policy enforcement uses, so a host cannot accept a policy set the runtime would later reject, nor reject one it would have honored. Call it when policies arrive from outside the runtime — a command-line flag, a configuration file, a host's own settings — and must be checked before a session exists to apply them to. A malformed document fails with a message prefixed `Invalid JSON: `; a well-formed document that violates the policy schema fails with the offending field paths.\n\nArgs:\n params: Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser.\n\nReturns:\n The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this result." 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))) + return ContentExclusionValidatePoliciesResult.from_dict(await self._client.request("contentExclusion.validatePolicies", params_dict, **_timeout_kwargs(timeout))) # Experimental: this API group is experimental and may change or be removed. @@ -55810,6 +61033,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"): @@ -55869,6 +61108,11 @@ async def plan_install(self, params: SkillPlanInstallRequest, *, timeout: float params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SkillInstallationManagementResult.from_dict(await self._client.request("skills.planInstall", params_dict, **_timeout_kwargs(timeout))) + async def plan_install_from_reference(self, params: SkillPlanInstallFromReferenceRequest, *, timeout: float | None = None) -> SkillInstallationManagementResult: + "Plans installation of the Skill pinned by an Agent Finder materialisation reference without writing files. The runtime retrieves the descriptor only from its fixed Agent Finder authority and applies the same verification as skills.planInstall; the review shows only verified data. Apply the returned plan with skills.applyInstall.\n\nArgs:\n params: Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate.\n\nReturns:\n Skill installation management result with the honoured contract, or a typed refusal." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return SkillInstallationManagementResult.from_dict(await self._client.request("skills.planInstallFromReference", params_dict, **_timeout_kwargs(timeout))) + async def apply_install(self, params: SkillApplyInstallRequest, *, timeout: float | None = None) -> SkillInstallationResult: "Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled.\n\nArgs:\n params: Applies exactly one retained verified Skill installation plan.\n\nReturns:\n Skill installation result with the honoured contract, or a typed request/negotiation refusal." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -56009,22 +61253,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"): @@ -56035,73 +61263,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 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 ServerRuntimeApi: def __init__(self, client: "JsonRpcClient"): @@ -56311,24 +61472,25 @@ 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.content_exclusion = ServerContentExclusionApi(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) @@ -56346,6 +61508,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"): @@ -56375,24 +61911,49 @@ 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: + 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))) + 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: + 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))) + 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: + 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))) + 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. @@ -56401,10 +61962,33 @@ def __init__(self, client: "JsonRpcClient"): self._client = client self.registry = _InternalServerMcpRegistryApi(client) - async def _should_exclude_git_hub_tools(self, params: MCPShouldExcludeGitHubToolsRequest, *, timeout: float | None = None) -> MCPShouldExcludeGitHubToolsResult: + 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))) + 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. @@ -56473,9 +62057,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." @@ -56569,8 +62153,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) @@ -56897,6 +62490,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 {} @@ -58750,25 +64349,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 @@ -59534,7 +65133,18 @@ async def handle_installations_confirm(params: dict) -> dict | None: "CatalogAgentPluginCandidateKind", "CatalogAgentPluginCandidateProvenance", "CatalogAgentPluginCompatibilityTag", + "CatalogAgentPluginComponent", + "CatalogAgentPluginInstallPlan", + "CatalogAgentPluginInstallSource", + "CatalogAgentPluginInstallSourceKind", + "CatalogAgentPluginInstallability", + "CatalogAgentPluginMarketplaceSource", + "CatalogAgentPluginMarketplaceSourceKind", + "CatalogAgentPluginMaterializationSource", + "CatalogAgentPluginMaterializationSourceKind", "CatalogAgentPluginMediaType", + "CatalogAgentPluginRepositorySource", + "CatalogAgentPluginRepositorySourceKind", "CatalogAuthenticationRequiredError", "CatalogAuthenticationRequiredErrorKind", "CatalogAuthenticationRequiredReason", @@ -59568,6 +65178,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "CatalogMalformedCardError", "CatalogMalformedCardErrorKind", "CatalogMalformedCardReason", + "CatalogMaterializationReference", "CatalogMcpServerInstallability", "CatalogMediaType", "CatalogNegotiatedContract", @@ -59708,6 +65319,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ContentExclusionCheckPathsRequest", "ContentExclusionCheckPathsResult", "ContentExclusionPathCheck", + "ContentExclusionValidatePoliciesRequest", + "ContentExclusionValidatePoliciesResult", "ContentFilterMode", "ContextHeaviestMessage", "CopilotAPITokenAuthInfo", @@ -59822,6 +65435,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ExternalToolTextResultForLlmContentTerminalType", "ExternalToolTextResultForLlmContentText", "ExternalToolTextResultForLlmContentType", + "FeatureFlagsResolveResult", + "FeatureFlagsResolveStartupGatesResult", "FilterMapping", "FleetApi", "FleetStartRequest", @@ -59834,7 +65449,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "GhCLIAuthInfoType", "GitHubAuthApi", "GitHubEnvironment", - "GitHubOwnersRequestIDResult", "GitHubRepositoryIdentity", "GitHubTelemetryClientInfo", "GitHubTelemetryEvent", @@ -59982,7 +65596,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPConfigList", "MCPConfigRemoveRequest", "MCPConfigUpdateRequest", - "MCPConfigureGitHubRequest", "MCPConfigureGitHubResult", "MCPConfiguredServer", "MCPConfiguredServerList", @@ -60099,14 +65712,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPPromptsGetResult", "MCPPromptsListRequest", "MCPPromptsListResult", - "MCPRegisterExternalClientRequest", - "MCPRegistryCancelRequest", - "MCPRegistryCancelResult", - "MCPRegistryRequestIDResult", - "MCPRegistrySearchRequest", - "MCPRegistrySearchResult", - "MCPReloadConfig", - "MCPReloadWithConfigRequest", "MCPRemoveGitHubResult", "MCPResource", "MCPResourceAnnotations", @@ -60137,8 +65742,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPServerConfigDeferTools", "MCPServerConfigHTTP", "MCPServerConfigHTTPType", - "MCPServerConfigMemory", - "MCPServerConfigMemoryType", "MCPServerConfigStdio", "MCPServerConfigStdioType", "MCPServerConfigType", @@ -60149,8 +65752,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPSetEnvValueModeDetails", "MCPSetEnvValueModeParams", "MCPSetEnvValueModeResult", - "MCPShouldExcludeGitHubToolsRequest", - "MCPShouldExcludeGitHubToolsResult", "MCPSourceFile", "MCPSourcePlugin", "MCPSourceRef", @@ -60162,7 +65763,6 @@ async def handle_installations_confirm(params: dict) -> dict | None: "MCPToolUIVisibility", "MCPTools", "MCPUninstallPlan", - "MCPUnregisterExternalClientRequest", "ManagedMCPServerConfig", "ManagedPermissionEvaluation", "ManagedPermissionOperation", @@ -60264,6 +65864,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ModelCapabilitiesOverrideLimitsVision", "ModelCapabilitiesOverrideSupports", "ModelCapabilitiesSupports", + "ModelGetReasoningSummariesRequest", + "ModelGetReasoningSummariesResult", "ModelList", "ModelListRequest", "ModelMessage", @@ -60431,6 +66033,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "PermissionsConfigureAdditionalContentExclusionPolicyScope", "PermissionsConfigureParams", "PermissionsConfigureResult", + "PermissionsEvaluateManagedPolicyResult", "PermissionsFolderTrustAddTrustedResult", "PermissionsFolderTrustApi", "PermissionsGetModeRequest", @@ -60469,6 +66072,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "PermissionsSetRequiredResult", "PermissionsUrlsApi", "PermissionsUrlsSetUnrestrictedModeResult", + "PersistenceWriteRawJSONLockedRequest", + "PersistenceWriteRawJSONLockedResult", "Phase", "PingRequest", "PingResult", @@ -60656,6 +66261,10 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ResponseFormatType", "ReviewClass", "SandboxApi", + "SandboxApplyDefaultsRequest", + "SandboxCheckEnforcementCommandResult", + "SandboxCheckPlatformSupportResult", + "SandboxCheckWindowsHostLoopbackSupportResult", "SandboxConfig", "SandboxConfigAuth", "SandboxConfigSource", @@ -60671,7 +66280,10 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SandboxCredentialsConfig", "SandboxDisableForSessionRequest", "SandboxDisableForSessionResult", + "SandboxDocsURLResult", "SandboxEnforcementStatus", + "SandboxGetUnsupportedMessageResult", + "SandboxGetUnsupportedSettingDisabledNoticeResult", "SandboxGrantPathForRequestRequest", "SandboxGrantPathForRequestResult", "SandboxHostCapability", @@ -60682,7 +66294,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", @@ -60714,6 +66334,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "ServerCatalogApi", "ServerCommandsApi", "ServerConnectorsApi", + "ServerContentExclusionApi", "ServerEnvironmentsApi", "ServerExtensionsApi", "ServerHooksApi", @@ -61001,6 +66622,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SkillInstallationSummary", "SkillInstallationsRequest", "SkillList", + "SkillPlanInstallFromReferenceRequest", "SkillPlanInstallRequest", "SkillPlanUninstallRequest", "SkillProviderDescriptor", @@ -61044,6 +66666,8 @@ async def handle_installations_confirm(params: dict) -> dict | None: "SlashCommandTextResult", "SlashCommandTimelineEntry", "SourceClass", + "StaffLogLevelMigrationStatus", + "StaffUpdateChannelMigrationStatus", "SubagentSettings", "SubagentSettingsEntry", "SubagentSettingsEntryContextTier", @@ -61168,6 +66792,7 @@ async def handle_installations_confirm(params: dict) -> dict | None: "UIUnregisterDirectAutoModeSwitchHandlerRequest", "UIUnregisterDirectAutoModeSwitchHandlerResult", "UIUserInputResponse", + "UNSET_SENTINEL", "UiApi", "UnsupportedEnqueueCommandResult", "UpdateSubagentSettingsRequest", @@ -61175,8 +66800,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 3ef3b61685..b205ef3232 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -5021,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": @@ -5035,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, @@ -5046,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: @@ -5070,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 @@ -7925,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": @@ -7935,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, @@ -7942,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: @@ -7952,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 @@ -8715,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 @@ -8727,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")) @@ -8736,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, @@ -8749,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: @@ -9459,8 +9484,6 @@ class SessionCompactionCompleteData: "Conversation compaction results including success status, metrics, and optional error details" success: bool # Internal: this field is an internal SDK API and is not part of the public surface. - _active_factory_summary: str | None = None - # Internal: this field is an internal SDK API and is not part of the public surface. _active_workflow_summary: str | None = None behavior_model_id: str | None = None checkpoint_number: int | None = None @@ -9488,7 +9511,6 @@ class SessionCompactionCompleteData: def from_dict(obj: Any) -> "SessionCompactionCompleteData": assert isinstance(obj, dict) success = from_bool(obj.get("success")) - _active_factory_summary = from_union([from_none, from_str], obj.get("activeFactorySummary")) _active_workflow_summary = from_union([from_none, from_str], obj.get("activeWorkflowSummary")) behavior_model_id = from_union([from_none, from_str], obj.get("behaviorModelId")) checkpoint_number = from_union([from_none, from_int], obj.get("checkpointNumber")) @@ -9513,7 +9535,6 @@ def from_dict(obj: Any) -> "SessionCompactionCompleteData": trigger = from_union([from_none, lambda x: parse_enum(CompactionTrigger, x)], obj.get("trigger")) return SessionCompactionCompleteData( success=success, - _active_factory_summary=_active_factory_summary, _active_workflow_summary=_active_workflow_summary, behavior_model_id=behavior_model_id, checkpoint_number=checkpoint_number, @@ -9541,8 +9562,6 @@ def from_dict(obj: Any) -> "SessionCompactionCompleteData": def to_dict(self) -> dict: result: dict = {} result["success"] = from_bool(self.success) - if self._active_factory_summary is not None: - result["activeFactorySummary"] = from_union([from_none, from_str], self._active_factory_summary) if self._active_workflow_summary is not None: result["activeWorkflowSummary"] = from_union([from_none, from_str], self._active_workflow_summary) if self.behavior_model_id is not None: @@ -9795,6 +9814,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 @@ -9809,6 +9830,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")) @@ -9820,6 +9843,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, @@ -9837,6 +9862,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: @@ -10490,6 +10519,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": @@ -10500,6 +10531,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, @@ -10507,6 +10540,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: @@ -10517,6 +10552,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 @@ -12085,7 +12124,6 @@ class SubagentStartedData: tool_call_id: str agent_type: str | None = None execution_mode: str | None = None - factory_run_id: str | None = None model: str | None = None model_selection_source: SubagentModelSelectionSource | None = None parent_id: str | None = None @@ -12102,7 +12140,6 @@ def from_dict(obj: Any) -> "SubagentStartedData": tool_call_id = from_str(obj.get("toolCallId")) agent_type = from_union([from_none, from_str], obj.get("agentType")) execution_mode = from_union([from_none, from_str], obj.get("executionMode")) - factory_run_id = from_union([from_none, from_str], obj.get("factoryRunId")) model = from_union([from_none, from_str], obj.get("model")) model_selection_source = from_union([from_none, lambda x: parse_enum(SubagentModelSelectionSource, x)], obj.get("modelSelectionSource")) parent_id = from_union([from_none, from_str], obj.get("parentId")) @@ -12116,7 +12153,6 @@ def from_dict(obj: Any) -> "SubagentStartedData": tool_call_id=tool_call_id, agent_type=agent_type, execution_mode=execution_mode, - factory_run_id=factory_run_id, model=model, model_selection_source=model_selection_source, parent_id=parent_id, @@ -12135,8 +12171,6 @@ def to_dict(self) -> dict: result["agentType"] = from_union([from_none, from_str], self.agent_type) if self.execution_mode is not None: result["executionMode"] = from_union([from_none, from_str], self.execution_mode) - if self.factory_run_id is not None: - result["factoryRunId"] = from_union([from_none, from_str], self.factory_run_id) if self.model is not None: result["model"] = from_union([from_none, from_str], self.model) if self.model_selection_source is not None: @@ -15870,6 +15904,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): diff --git a/python/copilot/session.py b/python/copilot/session.py index b61d753956..5b6bffb07d 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -11,6 +11,7 @@ import asyncio import functools import inspect +import json import logging import os import pathlib @@ -256,6 +257,22 @@ def _tool_handlers_for(tools: list[Tool] | None) -> dict[str, ToolHandler]: } +def _hook_error_text(error: Any) -> Any: + """Decode the ``error`` of an errorOccurred or sessionEnd hook input. + + The CLI sends an error object (``{"name", "message", "stack"}`` or + ``{"message"}``) where the hook input types declare a string: its message + becomes the error, and an object without a string message becomes its + compact JSON. Other values pass through. + """ + if isinstance(error, dict): + message = error.get("message") + if isinstance(message, str): + return message + return json.dumps(error, separators=(",", ":"), ensure_ascii=False) + return error + + def _consume_task_exception(task: asyncio.Task[Any]) -> None: if not task.cancelled(): task.exception() @@ -994,6 +1011,10 @@ class PreToolUseHookInput(TypedDict): toolName: str toolArgs: Any + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class PreToolUseHookOutput(TypedDict, total=False): """Output for pre-tool-use hook""" @@ -1023,6 +1044,10 @@ class PreMcpToolCallHookInput(TypedDict): toolCallId: NotRequired[str] _meta: NotRequired[dict[str, Any]] + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class PreMcpToolCallHookOutput(TypedDict, total=False): """Output for pre-MCP-tool-call hook. @@ -1052,6 +1077,10 @@ class PostToolUseHookInput(TypedDict): toolArgs: Any toolResult: Any + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class PostToolUseHookOutput(TypedDict, total=False): """Output for post-tool-use hook""" @@ -1082,6 +1111,10 @@ class PostToolUseFailureHookInput(TypedDict): toolArgs: Any error: str + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class PostToolUseFailureHookOutput(TypedDict, total=False): """Output for post-tool-use-failure hook. @@ -1107,6 +1140,10 @@ class UserPromptSubmittedHookInput(TypedDict): workingDirectory: str prompt: str + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class UserPromptSubmittedHookOutput(TypedDict, total=False): """Output for user-prompt-submitted hook""" @@ -1131,6 +1168,10 @@ class UserPromptTransformedHookInput(TypedDict): prompt: str transformedPrompt: str + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class UserPromptTransformedHookOutput(TypedDict, total=False): """Output for the user-prompt-transformed hook.""" @@ -1153,6 +1194,10 @@ class SessionStartHookInput(TypedDict): source: Literal["startup", "resume", "new"] initialPrompt: NotRequired[str] + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class SessionStartHookOutput(TypedDict, total=False): """Output for session-start hook""" @@ -1177,6 +1222,10 @@ class SessionEndHookInput(TypedDict): finalMessage: NotRequired[str] error: NotRequired[str] + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class SessionEndHookOutput(TypedDict, total=False): """Output for session-end hook""" @@ -1202,6 +1251,10 @@ class ErrorOccurredHookInput(TypedDict): errorContext: Literal["model_call", "tool_execution", "system", "user_input"] recoverable: bool + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class ErrorOccurredHookOutput(TypedDict, total=False): """Output for error-occurred hook""" @@ -1228,6 +1281,10 @@ class AgentStopHookInput(TypedDict): transcriptPath: NotRequired[str] stopHookActive: NotRequired[bool] + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class AgentStopHookOutput(TypedDict, total=False): """Output for the agent-stop hook.""" @@ -1253,6 +1310,10 @@ class SubagentStartHookInput(TypedDict): agentDisplayName: NotRequired[str] agentDescription: NotRequired[str] + agentId: NotRequired[str] + agentType: NotRequired[str] + parentAgentId: NotRequired[str] + class SubagentStartHookOutput(TypedDict, total=False): """Context prepended to the subagent's initial prompt.""" @@ -1281,6 +1342,8 @@ class SubagentStopHookInput(TypedDict): agentDisplayName: NotRequired[str] agentDescription: NotRequired[str] + parentAgentId: NotRequired[str] + class _SubagentStopBlockHookOutput(TypedDict): decision: Literal["block"] @@ -1373,6 +1436,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 @@ -3574,6 +3638,10 @@ async def _handle_hooks_invoke(self, hook_type: str, input_data: Any) -> Any: timestamp = transformed.get("timestamp") if isinstance(timestamp, (int, float)): transformed["timestamp"] = datetime.fromtimestamp(timestamp / 1000, tz=UTC) + # The CLI sends errorOccurred's and sessionEnd's ``error`` as an + # error object; the hook input types declare a string. + if hook_type in ("errorOccurred", "sessionEnd") and "error" in transformed: + transformed["error"] = _hook_error_text(transformed["error"]) # Each per-hook-type TypedDict is structurally compatible with the # normalized dict; cast to ``Any`` so ty doesn't try to narrow the # specific TypedDict variant from the runtime ``dict``. 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 95e80c1b0e..8180de01b5 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -1343,6 +1343,7 @@ async def mock_request(method, params, **kwargs): on_permission_request=PermissionHandler.approve_all, enable_managed_settings=True, enforce_managed_model_defaults=True, + interactive_user=True, managed_settings=ManagedSettings( permissions=ManagedSettingsPermissions( disable_bypass_permissions_mode=DisableBypassPermissionsModes.ALLOW_AUTO_ONLY, @@ -1358,6 +1359,7 @@ async def mock_request(method, params, **kwargs): session.session_id, on_permission_request=PermissionHandler.approve_all, enforce_managed_model_defaults=True, + interactive_user=True, managed_settings=ManagedSettings( permissions=ManagedSettingsPermissions( disable_bypass_permissions_mode="future-fail-closed-mode", @@ -1371,6 +1373,8 @@ async def mock_request(method, params, **kwargs): assert captured["session.create"]["enableManagedSettings"] is True assert captured["session.create"]["enforceManagedModelDefaults"] is True assert captured["session.resume"]["enforceManagedModelDefaults"] is True + assert captured["session.create"]["interactiveUser"] is True + assert captured["session.resume"]["interactiveUser"] is True assert captured["session.create"]["managedSettings"] == { "permissions": { "disableBypassPermissionsMode": "allow-auto-only", @@ -1404,6 +1408,39 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_explicit_interactive_user_false(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + interactive_user=False, + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + interactive_user=False, + ) + + assert captured["session.create"]["interactiveUser"] is False + assert captured["session.resume"]["interactiveUser"] is False + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_default_enable_experimental_mode_by_mode(self): with TemporaryDirectory() as base_directory: @@ -1441,6 +1478,8 @@ async def mock_request(method, params, **kwargs): assert captured["session.create"]["isExperimentalMode"] is False assert captured["session.resume"]["isExperimentalMode"] is False + assert "interactiveUser" not in captured["session.create"] + assert "interactiveUser" not in captured["session.resume"] finally: await client.force_stop() @@ -1466,6 +1505,7 @@ async def mock_request(method, params, **kwargs): ) assert "managedSettings" not in captured["session.create"] + assert "interactiveUser" not in captured["session.create"] finally: await client.force_stop() @@ -1938,6 +1978,49 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_github_cache_store(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + enable_github_cache_store=True, + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + enable_github_cache_store=False, + ) + assert captured["session.create"]["enableGitHubCacheStore"] is True + assert captured["session.resume"]["enableGitHubCacheStore"] is False + + unset = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + ) + await client.resume_session( + unset.session_id, + on_permission_request=PermissionHandler.approve_all, + ) + assert "enableGitHubCacheStore" not in captured["session.create"] + assert "enableGitHubCacheStore" not in captured["session.resume"] + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_forward_exp_assignments(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) @@ -3841,6 +3924,63 @@ def on_failure(input_data, invocation): assert result == {"additionalContext": "sync-ok"} +class TestHookErrorObjectNormalization: + """The CLI's error object reaches errorOccurred and sessionEnd handlers as a string.""" + + @pytest.mark.asyncio + @pytest.mark.parametrize( + ("error", "expected"), + [ + ( + {"name": "Error", "message": "model timeout", "stack": "Error: model timeout"}, + "model timeout", + ), + ("boom", "boom"), + (None, None), + ({"code": 7}, '{"code":7}'), + ], + ) + @pytest.mark.parametrize("hook_type", ["errorOccurred", "sessionEnd"]) + async def test_decodes_the_error(self, hook_type, error, expected): + from copilot.session import CopilotSession, SessionHooks + + captured: dict = {} + + def handler(input_data, invocation): + captured["input"] = input_data + + session = CopilotSession.__new__(CopilotSession) + CopilotSession.__init__(session, "sess-123", client=None) + session._hooks = SessionHooks( # type: ignore[typeddict-item] + on_error_occurred=handler, on_session_end=handler + ) + + await session._handle_hooks_invoke( + hook_type, + {"sessionId": "sess-x", "timestamp": 1700000000, "cwd": "/work", "error": error}, + ) + assert captured["input"]["error"] == expected + + @pytest.mark.asyncio + async def test_leaves_an_absent_session_end_error_absent(self): + from copilot.session import CopilotSession, SessionHooks + + captured: dict = {} + + def handler(input_data, invocation): + captured["input"] = input_data + + session = CopilotSession.__new__(CopilotSession) + CopilotSession.__init__(session, "sess-123", client=None) + session._hooks = SessionHooks(on_session_end=handler) # type: ignore[typeddict-item] + + await session._handle_hooks_invoke( + "sessionEnd", + {"sessionId": "sess-x", "timestamp": 1700000000, "cwd": "/work", "reason": "complete"}, + ) + assert "error" not in captured["input"] + + class TestAgentStopHookDispatch: """Unit tests for the agentStop handler dispatch.""" diff --git a/python/test_codegen_type_names.py b/python/test_codegen_type_names.py index 5242f1c786..c3a00cc044 100644 --- a/python/test_codegen_type_names.py +++ b/python/test_codegen_type_names.py @@ -27,3 +27,17 @@ def test_permission_approval_union_loaders_deserialize_expected_variants(): assert isinstance(session, rpc.PermissionDecisionApproveForSessionApprovalCommands) assert isinstance(location, rpc.PermissionDecisionApproveForLocationApprovalRead) + + +def test_catalog_candidate_provenance_serialises_every_catalog_media_type(): + assert not hasattr(rpc, "MediaType") + for media_type in rpc.CatalogMediaType: + provenance = rpc.CatalogCandidateProvenance( + authority="agentfinder.github.com", + media_type=media_type, + observed_at="2026-10-13T00:00:00.000Z", + ) + serialised = provenance.to_dict() + + assert serialised["mediaType"] == media_type.value + assert rpc.CatalogCandidateProvenance.from_dict(serialised) == provenance 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_subagent_hooks.py b/python/test_subagent_hooks.py index 71db1c6c00..5db29ecf30 100644 --- a/python/test_subagent_hooks.py +++ b/python/test_subagent_hooks.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. from datetime import UTC, datetime +from typing import NotRequired, get_origin, get_type_hints import pytest @@ -11,9 +12,52 @@ SubagentStopHookInput, SubagentStopHookOutput, ) +from copilot import session as session_types from copilot.session import CopilotSession +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("hook", "handler", "input_type"), + [ + ("preToolUse", "on_pre_tool_use", "PreToolUseHookInput"), + ("preMcpToolCall", "on_pre_mcp_tool_call", "PreMcpToolCallHookInput"), + ("postToolUse", "on_post_tool_use", "PostToolUseHookInput"), + ("postToolUseFailure", "on_post_tool_use_failure", "PostToolUseFailureHookInput"), + ("userPromptSubmitted", "on_user_prompt_submitted", "UserPromptSubmittedHookInput"), + ("userPromptTransformed", "on_user_prompt_transformed", "UserPromptTransformedHookInput"), + ("sessionStart", "on_session_start", "SessionStartHookInput"), + ("sessionEnd", "on_session_end", "SessionEndHookInput"), + ("errorOccurred", "on_error_occurred", "ErrorOccurredHookInput"), + ("agentStop", "on_agent_stop", "AgentStopHookInput"), + ("subagentStart", "on_subagent_start", "SubagentStartHookInput"), + ("subagentStop", "on_subagent_stop", "SubagentStopHookInput"), + ], +) +@pytest.mark.parametrize("enriched", [True, False]) +async def test_typed_hook_identity_retention_and_optional_declarations( + hook, handler, input_type, enriched +): + captured = [] + session = CopilotSession("callback-owner", None) + session._register_hooks({handler: lambda data, invocation: captured.append((data, invocation))}) + payload = {"sessionId": "reported-session", "timestamp": 1, "cwd": "work"} + identity = {"agentId": "child-actor", "agentType": "explore", "parentAgentId": "parent-actor"} + if enriched: + payload.update(identity) + await session._handle_hooks_invoke(hook, payload) + assert len(captured) == 1 + data, invocation = captured[0] + assert invocation == {"session_id": "callback-owner"} + assert data["sessionId"] == "reported-session" + declaration = getattr(session_types, input_type) + for key, value in identity.items(): + assert data.get(key) == (value if enriched else None) + assert key in declaration.__annotations__ + if not (hook == "subagentStop" and key == "agentType"): + assert get_origin(get_type_hints(declaration, include_extras=True)[key]) is NotRequired + + @pytest.mark.asyncio async def test_subagent_start_dispatches_normalized_input_and_context(): captured: list[tuple[SubagentStartHookInput, dict[str, str]]] = [] diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 74d6d98972..a5ef1e65ca 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1702,6 +1702,7 @@ version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ + "indexmap", "itoa", "memchr", "serde", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b82e322b02..741735b517 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -55,7 +55,7 @@ async-trait = "0.1" indexmap = { version = "2", features = ["serde"] } schemars = { version = "1", optional = true } serde = { version = "1", features = ["derive"] } -serde_json = "1" +serde_json = { version = "1", features = ["preserve_order"] } tokio = { version = "1", features = ["io-util", "sync", "rt", "process", "net", "time", "macros"] } tokio-stream = { version = "0.1", features = ["sync"] } tokio-util = { version = "0.7", default-features = false } @@ -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..48915f2dc6 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 | @@ -722,6 +728,12 @@ Use `with_ask_user_variant(AskUserVariant::Elicitation)` together with tool. The default remains `AskUserVariant::Legacy`. Re-supply the option and handler through `ResumeSessionConfig` on a cold resume. +Use `with_interactive_user(true)` to declare that a person types the session's +messages, as in an editor chat panel. This enables the runtime's user-message +analysis telemetry for user-authored messages; automated messages stay +excluded. The default is off, and cloud sessions ignore it. Re-supply it through +`ResumeSessionConfig` on a cold resume. + For rotating per-session GitHub credentials, install a `GitHubTokenProvider` instead of setting `github_token`: @@ -846,6 +858,29 @@ let session = client **Hook events:** `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmitted`, `UserPromptTransformed`, `SessionStart`, `SessionEnd`, `ErrorOccurred`, `AgentStop`, `SubagentStart`, and `SubagentStop`. Each carries typed input/output structs. `PostToolUse` only fires on success; override `on_post_tool_use_failure` to observe failed tool calls. `on_subagent_start` can inject `additional_context` into the child agent's initial prompt; `on_subagent_stop` can return a `decision` of `"block"` with a `reason` to continue the child or a `modified_response` to replace its final answer. Return `HookOutput::None` for events you don't handle. +To inspect runtime-provided actor metadata, override `SessionHooks::on_hook_with_actor`. +Its typed `HookActor` exposes optional `agent_id()`, `agent_type()`, and `parent_agent_id()` getters. +Root hooks and older runtimes can omit these fields; existing `SubagentStopInput` actor fields remain unchanged. +The default implementation delegates to `on_hook`, which retains the existing per-hook dispatch. +Existing implementations, input struct literals, and exact destructuring patterns therefore need no migration. +The enriched wire fields are opt-in metadata, not new fields on the legacy Rust input structs. + +```rust,no_run +# use async_trait::async_trait; +# use github_copilot_sdk::hooks::{HookActor, HookEvent, HookOutput, SessionHooks}; +struct ActorAwareHooks; + +#[async_trait] +impl SessionHooks for ActorAwareHooks { + async fn on_hook_with_actor(&self, event: HookEvent, actor: HookActor) -> HookOutput { + if let Some(id) = actor.agent_id() { + eprintln!("Hook actor: {id}"); + } + self.on_hook(event).await + } +} +``` + ### System Message Transforms Transforms customize system message sections during session creation. The SDK injects `action: "transform"` entries for each section ID your transform handles. @@ -1061,7 +1096,13 @@ let config = SessionConfig::default() .with_elicitation_handler(Arc::new(MyElicitation)); ``` -The handler receives a message, optional JSON Schema for form fields, and an optional mode. Known modes include `Form` and `Url`, but the mode may be absent or an unknown future value. +The handler receives a message, optional JSON Schema for form fields, and an optional mode. The schema retains the wire object's field order and metadata, including keys not modeled by the generated event type. Known modes include `Form` and `Url`, but the mode may be absent or an unknown future value. + +Wire-schema preservation is a Rust SDK guarantee, not a guarantee shared by +every SDK. For applications targeting multiple SDK languages, constrain form +schemas to the [portable elicitation schema contract](../README.md#portable-elicitation-schemas): +the modeled `type`, `properties`, and optional `required` fields, without +depending on additional top-level metadata or property insertion order. ### User Input Requests @@ -1206,6 +1247,8 @@ The CLI emits `session.compaction_start` / `session.compaction_complete` events `enable_session_store` on `SessionConfig` enables the cross-session store for search and retrieval across sessions. When unset in the default client mode, the runtime default applies (enabled). In `Empty` mode, defaults to disabled. +`enable_github_cache_store` on `SessionConfig` and `ResumeSessionConfig` (builder `with_enable_github_cache_store`) requests the read-only GitHub cache store (`github-cache-store` capability) on create or resume. The runtime registers the `github_sql` tool only when its `GITHUB_CACHE_STORE` feature flag is also on. Defaults to disabled in every mode. + ### Memory Configure the runtime memory feature for a session: diff --git a/rust/bazel/crates/BUILD.serde_json-1.0.151.bazel b/rust/bazel/crates/BUILD.serde_json-1.0.151.bazel index e02e3ef934..1a110fe834 100644 --- a/rust/bazel/crates/BUILD.serde_json-1.0.151.bazel +++ b/rust/bazel/crates/BUILD.serde_json-1.0.151.bazel @@ -41,6 +41,8 @@ rust_library( crate_features = [ "alloc", "default", + "indexmap", + "preserve_order", "std", ], crate_root = "src/lib.rs", @@ -70,6 +72,7 @@ rust_library( version = "1.0.151", deps = [ ":build_script_build", + "@sdk_vendor__indexmap-2.14.2//:indexmap", "@sdk_vendor__itoa-1.0.18//:itoa", "@sdk_vendor__memchr-2.8.3//:memchr", "@sdk_vendor__serde_core-1.0.229//:serde_core", @@ -102,6 +105,8 @@ cargo_build_script( crate_features = [ "alloc", "default", + "indexmap", + "preserve_order", "std", ], crate_name = "build_script_build", 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..eb87b09ced 100644 --- a/rust/src/ahp_host.rs +++ b/rust/src/ahp_host.rs @@ -925,6 +925,7 @@ macro_rules! host_settings { "featureFlags" => feature_flags, "enableManagedSettings" => enable_managed_settings, "enableSessionStore" => enable_session_store, + "enableGitHubCacheStore" => enable_github_cache_store, "availableTools" => available_tools, "excludedTools" => excluded_tools, "allowedModels" => allowed_models, @@ -936,6 +937,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..a008da5097 100644 --- a/rust/src/ahp_host/factory_tests.rs +++ b/rust/src/ahp_host/factory_tests.rs @@ -624,17 +624,20 @@ fn host_settings_round_trip_without_callbacks_or_losing_constraints() { "streaming": true, "gitHubToken": "test", "mcpOAuthTokenStorage": "in-memory", "enableConfigDiscovery": false, "featureFlags": {"flag": true}, "enableManagedSettings": false, "enableSessionStore": true, + "enableGitHubCacheStore": true, "availableTools": ["read_file"], "excludedTools": ["shell"], "allowedModels": ["model"], "systemMessage": {"mode":"append","content":"app"}, "skipCustomInstructions": true, "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(); assert!(config.permission_handler.is_none()); assert_eq!(config.config_directory, Some(PathBuf::from("/config"))); assert_eq!(config.enable_experimental_mode, Some(true)); + assert_eq!(config.enable_github_cache_store, Some(true)); assert_eq!( config.infinite_sessions.as_ref().unwrap().enabled, Some(false) @@ -647,6 +650,7 @@ fn host_settings_round_trip_without_callbacks_or_losing_constraints() { assert!(resume.permission_handler.is_none()); assert_eq!(resume.config_directory, Some(PathBuf::from("/config"))); assert_eq!(resume.enable_experimental_mode, Some(true)); + assert_eq!(resume.enable_github_cache_store, Some(true)); assert_eq!( resume.infinite_sessions.as_ref().unwrap().enabled, Some(false) 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/generated/api_types.rs b/rust/src/generated/api_types.rs index 577940cb1e..82ace5597b 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -23,6 +23,170 @@ 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` @@ -57,18 +221,8 @@ 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"; + /// `contentExclusion.validatePolicies` + pub const CONTENTEXCLUSION_VALIDATEPOLICIES: &str = "contentExclusion.validatePolicies"; /// `tools.list` pub const TOOLS_LIST: &str = "tools.list"; /// `account.getQuota` @@ -81,6 +235,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` @@ -105,6 +267,8 @@ pub mod rpc_methods { 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` @@ -131,8 +295,20 @@ 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.planInstallFromReference` + pub const SKILLS_PLANINSTALLFROMREFERENCE: &str = "skills.planInstallFromReference"; /// `skills.applyInstall` pub const SKILLS_APPLYINSTALL: &str = "skills.applyInstall"; /// `skills.installations.list` @@ -204,10 +380,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` @@ -216,10 +388,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` @@ -234,20 +402,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.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"; /// `runtime.shutdown` pub const RUNTIME_SHUTDOWN: &str = "runtime.shutdown"; /// `sessionFs.setProvider` @@ -467,6 +621,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` @@ -1775,6 +1931,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 /// ///
@@ -1794,6 +2011,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. /// ///
@@ -1858,6 +2111,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. /// ///
@@ -2126,6 +2428,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. @@ -2496,6 +2801,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. /// ///
@@ -2696,6 +3058,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 /// ///
@@ -3234,35 +3653,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. /// ///
@@ -4303,6 +4693,150 @@ impl CardDigest { } } +/// Agent Finder's verified materialisation of the plugin. Present only when the candidate carries a complete materialisation reference; the reference itself stays runtime-private. +/// +///
+/// +/// **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 CatalogAgentPluginMaterializationSource { + /// Discriminator: the plugin installs from Agent Finder's materialisation. + #[serde(deserialize_with = "CatalogAgentPluginMaterializationSource::deserialize_kind")] + pub kind: CatalogAgentPluginMaterializationSourceKind, +} + +impl CatalogAgentPluginMaterializationSource { + fn deserialize_kind<'de, D>( + deserializer: D, + ) -> Result + where + D: serde::Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + if value != "materialization" { + return Err(serde::de::Error::unknown_variant( + &value, + &["materialization"], + )); + } + ::deserialize( + serde::de::value::StringDeserializer::::new(value), + ) + } +} + +/// A plugin marketplace named by verified catalogue install data, installed as `pluginName@name`. +/// +///
+/// +/// **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 CatalogAgentPluginMarketplaceSource { + /// Discriminator: the plugin installs from a marketplace. + #[serde(deserialize_with = "CatalogAgentPluginMarketplaceSource::deserialize_kind")] + pub kind: CatalogAgentPluginMarketplaceSourceKind, + /// Marketplace name. + pub name: String, + /// Canonical lowercase GitHub owner/repository hosting the marketplace. + pub repository: String, +} + +impl CatalogAgentPluginMarketplaceSource { + fn deserialize_kind<'de, D>( + deserializer: D, + ) -> Result + where + D: serde::Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + if value != "marketplace" { + return Err(serde::de::Error::unknown_variant(&value, &["marketplace"])); + } + ::deserialize( + serde::de::value::StringDeserializer::::new(value), + ) + } +} + +/// A GitHub repository holding the plugin, installed directly as `owner/repo[:path]` at the plan's commit, without a marketplace. +/// +///
+/// +/// **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 CatalogAgentPluginRepositorySource { + /// Discriminator: the plugin installs directly from a repository. + #[serde(deserialize_with = "CatalogAgentPluginRepositorySource::deserialize_kind")] + pub kind: CatalogAgentPluginRepositorySourceKind, + /// Relative POSIX path of the plugin directory in the repository. Omitted when the plugin is at the repository root. + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + /// Canonical lowercase GitHub owner/repository holding the plugin. + pub repository: String, +} + +impl CatalogAgentPluginRepositorySource { + fn deserialize_kind<'de, D>( + deserializer: D, + ) -> Result + where + D: serde::Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + if value != "repository" { + return Err(serde::de::Error::unknown_variant(&value, &["repository"])); + } + ::deserialize( + serde::de::value::StringDeserializer::::new(value), + ) + } +} + +/// What installing an Agent Plugin would do, for the user to review and confirm. Provisional: the catalogue install data it is built from is not final. +/// +///
+/// +/// **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 CatalogAgentPluginInstallPlan { + /// Canvas IDs the plugin contributes. Non-empty exactly when components include `canvas`. + pub canvas_ids: Vec, + /// Exact full commit ID to install, in lowercase hex. Always present when the plan has a repository source; a plan with a marketplace source carries at least one of version and commit. + #[serde(skip_serializing_if = "Option::is_none")] + pub commit: Option, + /// Components the plugin contributes, in canonical order. + pub components: Vec, + /// Connectors the plugin depends on. + pub connector_dependencies: Vec, + /// Extension IDs the plugin contributes. Non-empty exactly when components include `extension`. + pub extension_ids: Vec, + /// MCP servers the plugin depends on but does not bundle. + pub mcp_dependencies: Vec, + /// The plugin's name, as named in its marketplace or manifest. + pub plugin_name: String, + /// Every valid install source, in preference order: materialization, then marketplace, then repository. Each kind appears at most once. + pub sources: Vec, + /// Exact plugin version to install. A plan with a marketplace source carries at least one of version and commit. + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, +} + /// Where and when an Agent Plugin catalog reference was observed. Discovery provenance deliberately carries no descriptor URL, raw data, candidate handle, or content digest. /// ///
@@ -4316,7 +4850,7 @@ impl CardDigest { pub struct CatalogAgentPluginCandidateProvenance { /// Host of the catalog authority that advertised the reference, without path, query, or credentials. Inert untrusted data. pub authority: String, - /// Canonical Agent Plugin media type. + /// Media type naming the plugin's format. pub media_type: CatalogAgentPluginMediaType, /// ISO 8601 timestamp at which the runtime observed the catalog reference. This is not a retrieval or validation timestamp. pub observed_at: String, @@ -4359,10 +4893,16 @@ pub struct CatalogAgentPluginCandidate { pub display_name: String, /// Validated, normalised catalogue resource URN. This identity comes only from the catalog identifier and is never inferred from display text or installed-plugin state. pub identity: String, + /// Whether this plugin can be installed in the selected session. Present only when the caller requires the agent-plugin-confirmed-installation capability. + #[serde(skip_serializing_if = "Option::is_none")] + pub installability: Option, + /// Reviewable install plan built only from verified catalogue install data. Present exactly when installability is `installable`; the runtime never derives it from the repository source, display text, or identity. + #[serde(skip_serializing_if = "Option::is_none")] + pub install_plan: Option, /// Discriminator: this candidate describes an Agent Plugin. #[serde(deserialize_with = "CatalogAgentPluginCandidate::deserialize_kind")] pub kind: CatalogAgentPluginCandidateKind, - /// Canonical Agent Plugin media type. + /// Media type naming the plugin's format. pub media_type: CatalogAgentPluginMediaType, /// Where the Agent Plugin catalog reference was observed, without its descriptor, URL, raw data, or content digest. pub provenance: CatalogAgentPluginCandidateProvenance, @@ -4727,6 +5267,33 @@ pub struct CatalogMalformedCardError { pub reason: CatalogMalformedCardReason, } +/// An Agent Finder materialisation reference pinning one exact resource revision. +/// +/// It carries no origin or URL: the runtime always builds the descriptor URL on its fixed Agent +/// Finder authority. It carries no display data either; every reviewed field comes from the +/// verified descriptor and Skill files. +/// +///
+/// +/// **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 CatalogMaterializationReference { + /// Expected `sha256:` digest of the descriptor's bundle manifest. + pub bundle_digest: String, + /// Expected `sha256:` digest of the canonical descriptor. + pub descriptor_digest: String, + /// Agent Finder resource identifier, a positive decimal integer. + pub resource_id: String, + /// Agent Finder materialisation revision identifier, a positive decimal integer. This is not a Git commit. + pub revision_id: String, + /// Materialisation descriptor schema version, currently `1.0.0`. + pub schema_version: String, +} + /// The protocol version and capability set the runtime actually honoured for a successful catalog operation. /// ///
@@ -6597,6 +7164,36 @@ pub struct ContentExclusionCheckPathsResult { pub checks: Vec, } +/// Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser. +/// +///
+/// +/// **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 ContentExclusionValidatePoliciesRequest { + /// Unparsed JSON text expected to hold an array of content-exclusion policies. + pub policies_json: String, +} + +/// The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this 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 ContentExclusionValidatePoliciesResult { + /// Validated policy objects in input order. Raw JSON preserves every field returned by the runtime's policy parser. + pub policies: Vec, +} + /// A single large message currently in context. /// ///
@@ -8219,6 +8816,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. /// ///
@@ -8305,6 +9024,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. /// ///
@@ -9295,6 +10062,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. /// ///
@@ -10643,6 +11467,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. /// ///
@@ -10662,6 +11516,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. /// ///
@@ -10681,6 +11592,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. /// ///
@@ -10733,6 +11701,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. /// ///
@@ -11031,6 +12014,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. /// ///
@@ -11230,6 +12228,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. /// ///
@@ -11418,6 +12449,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. /// ///
@@ -11437,6 +12734,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. /// ///
@@ -11493,6 +12899,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. /// ///
@@ -13010,6 +14582,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. /// ///
@@ -15988,6 +17575,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. /// ///
@@ -18443,6 +20079,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. /// ///
@@ -18770,6 +20441,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. /// ///
@@ -21095,6 +22804,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. /// ///
@@ -21112,6 +22925,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. /// ///
@@ -21422,6 +23250,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. /// ///
@@ -21442,6 +23335,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. /// ///
@@ -21515,6 +23461,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. /// ///
@@ -21566,6 +23546,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. /// ///
@@ -23606,6 +25718,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, @@ -23686,7 +25801,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. @@ -23728,6 +25843,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>, @@ -23740,9 +25858,15 @@ 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, + /// Declares that a human drives this session's conversation, enabling user-message analysis (sentiment, task completion criteria, and task analytics) for user-authored messages. Automated messages remain excluded. Defaults to false for SDK sessions. Applied when the session is created or loaded from storage; ignored when resuming a session that is already active, and for cloud sessions. + #[serde(skip_serializing_if = "Option::is_none")] + pub interactive_user: Option, /// Whether experimental behavior is enabled. #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, @@ -23857,7 +25981,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. @@ -25479,6 +27603,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, @@ -25515,7 +27642,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). @@ -25533,7 +27660,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. @@ -25557,6 +27684,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>, @@ -25569,6 +27699,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, @@ -25638,7 +27771,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. @@ -25682,6 +27815,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. /// ///
@@ -26268,6 +28417,25 @@ pub struct SkillList { pub skills: Vec, } +/// Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate. +/// +///
+/// +/// **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 SkillPlanInstallFromReferenceRequest { + /// Required authenticated bound catalogue, skill-confirmed-installation and skill-reference-installation capabilities. + pub contract: CatalogClientContract, + /// Existing local session attached to this connection. + pub policy_session_id: String, + /// Materialisation reference to verify and plan. + pub reference: CatalogMaterializationReference, +} + /// Side-effect-free planning of one verified Agent Finder Skill candidate. /// ///
@@ -26446,7 +28614,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>, } @@ -26509,6 +28677,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. /// ///
@@ -28787,6 +31019,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. /// ///
@@ -28802,6 +31181,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. /// ///
@@ -28817,6 +31739,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. /// ///
@@ -30491,7 +33536,7 @@ pub struct WorkspacesWriteAutopilotObjectiveResult { pub operation: String, } -/// The resident session the application has materialized on its own connection. +/// Status of the persistent certificate authority of the sandbox credential proxy. /// ///
/// @@ -30501,12 +33546,17 @@ pub struct WorkspacesWriteAutopilotObjectiveResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HostCreateSessionResult { - /// Runtime session UUID materialized on the application's SDK connection. - pub session_id: SessionId, +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, } -/// Empty acknowledgement for a completed host lifecycle operation. +/// Status of the persistent certificate authority of the sandbox credential proxy. /// ///
/// @@ -30516,9 +33566,17 @@ pub struct HostCreateSessionResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HostReleaseSessionResult {} +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, +} -/// Empty acknowledgement for a completed host lifecycle operation. +/// Status of the persistent certificate authority of the sandbox credential proxy. /// ///
/// @@ -30528,9 +33586,17 @@ pub struct HostReleaseSessionResult {} ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HostDisposeResult {} +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, +} -/// Private credentials delivered only to a runtime-owned Mission Control hosting participant. +/// Status of the persistent certificate authority of the sandbox credential proxy. /// ///
/// @@ -30540,18 +33606,17 @@ pub struct HostDisposeResult {} ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HostGetEnvironmentCredentialsResult { - /// GitHub API base URL for the authenticated service. - pub github_api_url: String, - /// Hostname of the authenticated GitHub service. - pub github_host: String, - /// Mission Control API origin for environment registration and management. - pub mission_control_url: String, - /// Current bearer token for the authenticated GitHub identity. - pub token: String, +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, } -/// Normalized listener settings delivered only to the supervised hosting participant. +/// Where this runtime keeps the sandbox proxy password. /// ///
/// @@ -30561,20 +33626,14 @@ pub struct HostGetEnvironmentCredentialsResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HostGetConfigurationResult { - /// Requested GitHub Mission Control registration. - #[serde(skip_serializing_if = "Option::is_none")] - pub github_environment: Option, - /// Normalized local listener settings, absent for relay-only hosts. - #[serde(skip_serializing_if = "Option::is_none")] - pub local_server: Option, - /// Whether app-owned durable sessions are resumed by the owning application. - pub resume_factory: bool, - /// Whether session materialization is delegated to the owning application. - pub session_factory: bool, +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, } -/// Empty acknowledgement for a completed host lifecycle operation. +/// The sandbox a named feature actually launches under. /// ///
/// @@ -30584,9 +33643,12 @@ pub struct HostGetConfigurationResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HostReadyResult {} +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, +} -/// List of Copilot models available to the resolved user, including capabilities and billing metadata. +/// The startup warning, absent when the machine can run a sandbox. /// ///
/// @@ -30596,12 +33658,13 @@ pub struct HostReadyResult {} ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ModelsListResult { - /// List of available models with full metadata - pub models: Vec, +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 running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. +/// The documentation URL a host cites when it declines or downgrades a sandbox setting. /// ///
/// @@ -30611,9 +33674,9 @@ pub struct ModelsListResult { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ModelsGetBuiltInCatalogResult { - /// Built-in model entries. - pub models: Vec, +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. @@ -30636,7 +33699,7 @@ pub struct SandboxGetHostSupportResult { pub supported: bool, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// The on-disk location of the user's settings document. /// ///
/// @@ -30646,17 +33709,28 @@ 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. +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 detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, + pub warning: Option, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// Top-level user-setting keys grouped by who may govern them. /// ///
/// @@ -30666,17 +33740,48 @@ 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. +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 detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, + pub problems: Option>, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// Outcome of an atomic user or repository settings edit. /// ///
/// @@ -30686,17 +33791,89 @@ 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. +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 detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, + 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, } -/// Status of the persistent certificate authority of the sandbox credential proxy. +/// Ordered managed permission verdicts and the supplied policy's fail-closed posture. /// ///
/// @@ -30706,14 +33883,136 @@ 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. +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. +/// +///
+/// +/// **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 HostCreateSessionResult { + /// Runtime session UUID materialized on the application's SDK connection. + pub session_id: SessionId, +} + +/// Empty acknowledgement for a completed host lifecycle operation. +/// +///
+/// +/// **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 HostReleaseSessionResult {} + +/// Empty acknowledgement for a completed host lifecycle operation. +/// +///
+/// +/// **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 HostDisposeResult {} + +/// Private credentials delivered only to a runtime-owned Mission Control hosting participant. +/// +///
+/// +/// **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 HostGetEnvironmentCredentialsResult { + /// GitHub API base URL for the authenticated service. + pub github_api_url: String, + /// Hostname of the authenticated GitHub service. + pub github_host: String, + /// Mission Control API origin for environment registration and management. + pub mission_control_url: String, + /// Current bearer token for the authenticated GitHub identity. + pub token: String, +} + +/// Normalized listener settings delivered only to the supervised hosting participant. +/// +///
+/// +/// **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 HostGetConfigurationResult { + /// Requested GitHub Mission Control registration. #[serde(skip_serializing_if = "Option::is_none")] - pub detail: Option, - /// The state of the certificate authority. - pub state: SandboxProxyCaState, + pub github_environment: Option, + /// Normalized local listener settings, absent for relay-only hosts. + #[serde(skip_serializing_if = "Option::is_none")] + pub local_server: Option, + /// Whether app-owned durable sessions are resumed by the owning application. + pub resume_factory: bool, + /// Whether session materialization is delegated to the owning application. + pub session_factory: bool, +} + +/// Empty acknowledgement for a completed host lifecycle operation. +/// +///
+/// +/// **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 HostReadyResult {} + +/// List of Copilot models available to the resolved user, including capabilities and billing metadata. +/// +///
+/// +/// **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 ModelsListResult { + /// List of available models with full metadata + pub models: Vec, +} + +/// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. +/// +///
+/// +/// **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 ModelsGetBuiltInCatalogResult { + /// Built-in model entries. + pub models: Vec, } /// Built-in tools available for the requested model, with their parameters and instructions. @@ -30778,6 +34077,66 @@ pub struct ExtensionsDiscoverResult { pub mode: DiscoveredExtensionMode, } +/// 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 InstructionsGetInitPromptResult { + /// Prompt text authoring repository instructions. + pub prompt: String, +} + +/// 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, +} + +/// 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 InstructionsDiscoverRepoSourcesResult { + /// 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, +} + /// Skills discovered across global and project sources. /// ///
@@ -30996,36 +34355,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. /// ///
@@ -31164,23 +34493,6 @@ pub struct GitWorkingDirectoryContextResult { pub repository_host: Option, } -/// 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, -} - /// Result of opening a session. /// ///
@@ -32781,6 +36093,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. /// ///
@@ -38302,6 +41644,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 /// ///
@@ -38527,6 +41989,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. /// ///
@@ -39210,49 +42715,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 { @@ -39495,6 +42957,147 @@ pub enum CatalogAgentPluginCompatibilityTag { Unknown, } +/// Typed installability state for an Agent Plugin candidate +/// +///
+/// +/// **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 CatalogAgentPluginInstallability { + /// The candidate carries complete, verified install data and the selected session may install it. Its install plan is present. + #[serde(rename = "installable")] + Installable, + /// The catalogue supplied no complete, verified install data, so no install target is offered. + #[serde(rename = "install-data-unavailable")] + InstallDataUnavailable, + /// Plugin installation is understood but disabled for the selected session. + #[serde(rename = "feature-disabled")] + FeatureDisabled, + /// Reserved for plugin formats this runtime cannot install. Every format surfaced today installs through a marketplace. + #[serde(rename = "not-installable-kind")] + NotInstallableKind, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// A kind of component an Agent Plugin contributes +/// +///
+/// +/// **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 CatalogAgentPluginComponent { + /// Custom agents. + #[serde(rename = "agent")] + Agent, + /// Slash commands. + #[serde(rename = "command")] + Command, + /// Skills. + #[serde(rename = "skill")] + Skill, + /// Hooks. + #[serde(rename = "hook")] + Hook, + /// Bundled MCP servers. + #[serde(rename = "mcp-server")] + McpServer, + /// Bundled LSP servers. + #[serde(rename = "lsp-server")] + LspServer, + /// Extensions. + #[serde(rename = "extension")] + Extension, + /// Canvases. + #[serde(rename = "canvas")] + Canvas, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Discriminator for a materialisation install source +/// +///
+/// +/// **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 CatalogAgentPluginMaterializationSourceKind { + /// Install from Agent Finder's verified materialisation. + #[serde(rename = "materialization")] + Materialization, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Discriminator for a marketplace install source +/// +///
+/// +/// **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 CatalogAgentPluginMarketplaceSourceKind { + /// Install from a plugin marketplace. + #[serde(rename = "marketplace")] + Marketplace, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Discriminator for a repository install source +/// +///
+/// +/// **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 CatalogAgentPluginRepositorySourceKind { + /// Install directly from a GitHub repository. + #[serde(rename = "repository")] + Repository, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// One place an Agent Plugin can install from: Agent Finder's materialisation, a marketplace, or a GitHub repository. Each variant carries only its own fields. +/// +///
+/// +/// **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, Deserialize)] +#[serde(untagged)] +pub enum CatalogAgentPluginInstallSource { + Materialization(CatalogAgentPluginMaterializationSource), + Marketplace(CatalogAgentPluginMarketplaceSource), + Repository(CatalogAgentPluginRepositorySource), +} + /// Discriminator for an Agent Plugin candidate /// ///
@@ -39514,7 +43117,7 @@ pub enum CatalogAgentPluginCandidateKind { Unknown, } -/// Canonical Agent Plugin media type +/// Media type naming the plugin format of an Agent Plugin candidate. Values other than `application/vnd.github.copilot-plugin` appear only when the caller requires the agent-plugin-formats capability. /// ///
/// @@ -39527,6 +43130,12 @@ pub enum CatalogAgentPluginMediaType { /// A GitHub Copilot Agent Plugin descriptor. #[serde(rename = "application/vnd.github.copilot-plugin")] ApplicationVndGitHubCopilotPlugin, + /// A Claude plugin descriptor (`.claude-plugin/plugin.json` layout). + #[serde(rename = "application/vnd.anthropic.claude-plugin+json")] + ApplicationVndAnthropicClaudePluginJson, + /// A portable Agent Plugin descriptor. + #[serde(rename = "application/agent-plugin+json")] + ApplicationAgentPluginJson, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -39792,6 +43401,9 @@ pub enum CatalogCapability { /// Understands opt-in `application/vnd.github.copilot-plugin` candidates and their typed identity, version, source, and compatibility fields. #[serde(rename = "agent-plugin-discovery")] AgentPluginDiscovery, + /// Extends agent-plugin-discovery to Claude plugin (`application/vnd.anthropic.claude-plugin+json`) and portable Agent Plugin (`application/agent-plugin+json`) candidates, each reporting its format as its media type. Requires agent-plugin-discovery; without it, plugin searches return only Copilot plugins. + #[serde(rename = "agent-plugin-formats")] + AgentPluginFormats, /// Understands side-effect-free MCP install-plan requests, results, and plan handles; `planning-unavailable` separately reports that planning is not enabled. #[serde(rename = "mcp-install-planning")] McpInstallPlanning, @@ -39822,6 +43434,12 @@ pub enum CatalogCapability { /// Understands verified Agent Finder Skill install, uninstall, recovery and installation-scoped enablement APIs. Advertised only by runtimes with the Skill installation engine linked; acquisition may still be refused as feature-disabled per selected session. #[serde(rename = "skill-confirmed-installation")] SkillConfirmedInstallation, + /// Understands `installability` and the reviewable `installPlan` on Agent Plugin candidates. Requires agent-plugin-discovery; without it, plugin candidates are unchanged. A plugin is installable only with complete, verified catalogue install data, and may still be reported as feature-disabled per selected session. + #[serde(rename = "agent-plugin-confirmed-installation")] + AgentPluginConfirmedInstallation, + /// Extends skill-confirmed-installation to planning from an Agent Finder materialisation reference through skills.planInstallFromReference, and to its distinct reference failure reasons. Requires skill-confirmed-installation. Advertised only by runtimes with the Skill installation engine linked. + #[serde(rename = "skill-reference-installation")] + SkillReferenceInstallation, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -40021,6 +43639,12 @@ pub enum CatalogMediaType { /// An inert Agent Plugin descriptor. #[serde(rename = "application/vnd.github.copilot-plugin")] ApplicationVndGitHubCopilotPlugin, + /// An inert Claude plugin descriptor, surfaced only under the agent-plugin-formats capability. + #[serde(rename = "application/vnd.anthropic.claude-plugin+json")] + ApplicationVndAnthropicClaudePluginJson, + /// An inert portable Agent Plugin descriptor, surfaced only under the agent-plugin-formats capability. + #[serde(rename = "application/agent-plugin+json")] + ApplicationAgentPluginJson, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -41989,6 +45613,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 { @@ -42969,6 +46636,56 @@ pub enum ManagedPluginRetryStatus { 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. /// ///
@@ -43016,6 +46733,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. /// ///
@@ -45814,6 +49668,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 { @@ -45962,6 +49866,9 @@ pub enum SessionCapability { /// Cross-session history tools and session-store SQL prompt/tool metadata. #[serde(rename = "session-store")] SessionStore, + /// Read-only GitHub cache SQL tool (github_sql). + #[serde(rename = "github-cache-store")] + GitHubCacheStore, /// MCP Apps UI passthrough. #[serde(rename = "mcp-apps")] McpApps, @@ -46749,6 +50656,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) /// ///
@@ -46899,6 +50831,21 @@ pub enum SkillInstallationFailureReason { /// A storage operation failed; inspect durable state before retrying. #[serde(rename = "write-failed")] WriteFailed, + /// The materialisation reference is malformed or uses an unsupported schema version. Returned only to callers that require skill-reference-installation. + #[serde(rename = "invalid-reference")] + InvalidReference, + /// Agent Finder has no materialisation for the referenced resource revision (HTTP 404). Returned only to callers that require skill-reference-installation. + #[serde(rename = "descriptor-not-found")] + DescriptorNotFound, + /// Agent Finder withdrew the referenced resource revision (HTTP 410). Returned only to callers that require skill-reference-installation. + #[serde(rename = "descriptor-withdrawn")] + DescriptorWithdrawn, + /// The retrieved descriptor does not match the referenced descriptor or bundle digest. Returned only to callers that require skill-reference-installation. + #[serde(rename = "digest-mismatch")] + DigestMismatch, + /// The referenced resource is not a Skill. Returned only to callers that require skill-reference-installation. + #[serde(rename = "unsupported-resource")] + UnsupportedResource, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -47277,6 +51224,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 /// ///
@@ -47860,6 +51863,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 e2b69d3933..b3babe4378 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -64,6 +64,13 @@ impl<'a> ClientRpc<'a> { } } + /// `contentExclusion.*` sub-namespace. + pub fn content_exclusion(&self) -> ClientRpcContentExclusion<'a> { + ClientRpcContentExclusion { + client: self.client, + } + } + /// `environments.*` sub-namespace. pub fn environments(&self) -> ClientRpcEnvironments<'a> { ClientRpcEnvironments { @@ -78,6 +85,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 +169,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 +190,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 +498,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 +691,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 +755,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`. @@ -975,6 +1193,48 @@ impl<'a> ClientRpcConnectors<'a> { } } +/// `contentExclusion.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcContentExclusion<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcContentExclusion<'a> { + /// Validates candidate content-exclusion policies and returns them normalized, without starting a session. This runs the same parser the runtime's own policy enforcement uses, so a host cannot accept a policy set the runtime would later reject, nor reject one it would have honored. Call it when policies arrive from outside the runtime — a command-line flag, a configuration file, a host's own settings — and must be checked before a session exists to apply them to. A malformed document fails with a message prefixed `Invalid JSON: `; a well-formed document that violates the policy schema fails with the offending field paths. + /// + /// Wire method: `contentExclusion.validatePolicies`. + /// + /// # Parameters + /// + /// * `params` - Candidate content-exclusion policies to validate, as the raw JSON text a host received. The text is supplied unparsed so that a syntax error and a schema violation are both reported by the runtime's own validator rather than by the caller's JSON parser. + /// + /// # Returns + /// + /// The accepted policies, parsed and normalized. A rejected input is reported as an error rather than in this 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 validate_policies( + &self, + params: ContentExclusionValidatePoliciesRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::CONTENTEXCLUSION_VALIDATEPOLICIES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `environments.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcEnvironments<'a> { @@ -1156,24 +1416,24 @@ impl<'a> ClientRpcExtensions<'a> { } } -/// `git.*` RPCs. +/// `featureFlags.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcGit<'a> { +pub struct ClientRpcFeatureFlags<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcGit<'a> { - /// Reads the remote that the branch checked out in a working tree tracks, as `branch..remote` configures it. Reports `origin` rather than failing whenever there is no tracking configuration to read — on a detached HEAD, on a branch with no upstream, or when git itself fails — because a caller asking which remote to talk to needs an answer it can act on, not an error. 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. +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: `git.currentBranchRemote`. + /// Wire method: `featureFlags.resolveStartupGates`. /// /// # Parameters /// - /// * `params` - Working-tree path a git query applies to. + /// * `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 remote the checked-out branch tracks. + /// The resolved gates, keyed by flag name. /// ///
/// @@ -1182,29 +1442,28 @@ impl<'a> ClientRpcGit<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn current_branch_remote( + pub(crate) async fn resolve_startup_gates( &self, - params: GitCwdRequest, - ) -> Result { + params: FeatureFlagsResolveStartupGatesRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::GIT_CURRENTBRANCHREMOTE, Some(wire_params)) + .call( + rpc_methods::FEATUREFLAGS_RESOLVESTARTUPGATES, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Collects the repository context of a working directory in one call: working tree root, repository identifier and host, current branch, and the HEAD and base commits. Every repository field is omitted when the path is not inside a git working tree, and the requested path is echoed back as `cwd`. The answer is the same `SessionWorkingDirectoryContext` that `session.metadata.recordContextChange` accepts, so a caller polling for a context change can forward the result unchanged. 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 derive session context from a directory itself. - /// - /// Wire method: `git.workingDirectoryContext`. - /// - /// # Parameters + /// 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. /// - /// * `params` - Working-tree path a git query applies to. + /// Wire method: `featureFlags.listMetadata`. /// /// # Returns /// - /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. + /// Every feature flag this runtime declares, with the shape each one was declared with. /// ///
/// @@ -1213,29 +1472,26 @@ impl<'a> ClientRpcGit<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn working_directory_context( - &self, - params: GitCwdRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub(crate) async fn list_metadata(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::GIT_WORKINGDIRECTORYCONTEXT, Some(wire_params)) + .call(rpc_methods::FEATUREFLAGS_LISTMETADATA, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists the GitHub repositories a working tree's remotes point at, one entry per distinct repository, so a caller can resolve a base and head repository without parsing remote URLs itself. When several remotes name the same repository, only the first is listed, and the entry keeps that remote name. Remotes pointing at no GitHub host are left out, so an empty list means the tree reaches GitHub through no remote. Failing to read the remotes is reported as an error rather than as an empty list, because the two mean different things to a caller. 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. + /// 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: `git.reposFromRemotes`. + /// Wire method: `featureFlags.resolve`. /// /// # Parameters /// - /// * `params` - Git working tree whose GitHub remotes should be listed. + /// * `params` - Caller population and overrides for static feature-flag resolution, independent of experiment assignments and active runtime state. /// /// # Returns /// - /// The GitHub repositories a working tree's remotes point at. + /// The resolved feature flags, keyed by flag name. /// ///
/// @@ -1244,33 +1500,37 @@ impl<'a> ClientRpcGit<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn repos_from_remotes( + pub(crate) async fn resolve( &self, - params: GitReposFromRemotesRequest, - ) -> Result { + params: FeatureFlagsResolveRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::GIT_REPOSFROMREMOTES, Some(wire_params)) + .call(rpc_methods::FEATUREFLAGS_RESOLVE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } } -/// `gitHubOwners.*` RPCs. +/// `git.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcGitHubOwners<'a> { +pub struct ClientRpcGit<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcGitHubOwners<'a> { - /// 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. +impl<'a> ClientRpcGit<'a> { + /// Reads the remote that the branch checked out in a working tree tracks, as `branch..remote` configures it. Reports `origin` rather than failing whenever there is no tracking configuration to read — on a detached HEAD, on a branch with no upstream, or when git itself fails — because a caller asking which remote to talk to needs an answer it can act on, not an error. 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: `gitHubOwners.nextRequestId`. + /// Wire method: `git.currentBranchRemote`. + /// + /// # Parameters + /// + /// * `params` - Working-tree path a git query applies to. /// /// # Returns /// - /// 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. + /// The remote the checked-out branch tracks. /// ///
/// @@ -1279,12 +1539,109 @@ impl<'a> ClientRpcGitHubOwners<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn next_request_id(&self) -> Result { - let wire_params = serde_json::json!({}); - let _value = self - .client - .call(rpc_methods::GITHUBOWNERS_NEXTREQUESTID, Some(wire_params)) - .await?; + pub(crate) async fn current_branch_remote( + &self, + params: GitCwdRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::GIT_CURRENTBRANCHREMOTE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Collects the repository context of a working directory in one call: working tree root, repository identifier and host, current branch, and the HEAD and base commits. Every repository field is omitted when the path is not inside a git working tree, and the requested path is echoed back as `cwd`. The answer is the same `SessionWorkingDirectoryContext` that `session.metadata.recordContextChange` accepts, so a caller polling for a context change can forward the result unchanged. 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 derive session context from a directory itself. + /// + /// Wire method: `git.workingDirectoryContext`. + /// + /// # Parameters + /// + /// * `params` - Working-tree path a git query applies to. + /// + /// # Returns + /// + /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. + /// + ///
+ /// + /// **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 working_directory_context( + &self, + params: GitCwdRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::GIT_WORKINGDIRECTORYCONTEXT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Lists the GitHub repositories a working tree's remotes point at, one entry per distinct repository, so a caller can resolve a base and head repository without parsing remote URLs itself. When several remotes name the same repository, only the first is listed, and the entry keeps that remote name. Remotes pointing at no GitHub host are left out, so an empty list means the tree reaches GitHub through no remote. Failing to read the remotes is reported as an error rather than as an empty list, because the two mean different things to a caller. 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: `git.reposFromRemotes`. + /// + /// # Parameters + /// + /// * `params` - Git working tree whose GitHub remotes should be listed. + /// + /// # Returns + /// + /// The GitHub repositories a working tree's remotes point at. + /// + ///
+ /// + /// **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 repos_from_remotes( + &self, + params: GitReposFromRemotesRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::GIT_REPOSFROMREMOTES, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `gitHubOwners.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcGitHubOwners<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcGitHubOwners<'a> { + /// 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. + /// + /// Wire method: `gitHubOwners.nextRequestId`. + /// + /// # Returns + /// + /// 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.** 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 next_request_id(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::GITHUBOWNERS_NEXTREQUESTID, Some(wire_params)) + .await?; Ok(serde_json::from_value(_value)?) } @@ -1514,6 +1871,39 @@ impl<'a> ClientRpcHooks<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + /// Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. + /// + /// Wire method: `hooks.discover`. + /// + /// # Parameters + /// + /// * `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. + /// + ///
+ /// + /// **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 [`HooksDiscoverOptions`], including inputs added after [`HooksDiscoverRequest`]. + pub async fn discover_with_options( + &self, + 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. @@ -1825,6 +2215,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,17 +2270,17 @@ impl<'a> ClientRpcInstructions<'a> { 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. + /// Discovers instruction sources across user, repository, and plugin sources. /// - /// Wire method: `instructions.getDiscoveryPaths`. + /// Wire method: `instructions.discover`. /// /// # Parameters /// - /// * `params` - Optional project paths to include when enumerating instruction discovery targets. + /// * `params` - Optional project paths to include in instruction discovery. /// /// # Returns /// - /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. + /// Instruction sources discovered across user, repository, and plugin sources. /// ///
/// @@ -1875,36 +2289,31 @@ impl<'a> ClientRpcInstructions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_discovery_paths( + /// + /// Accepts [`InstructionsDiscoverOptions`], including inputs added after [`InstructionsDiscoverRequest`]. + pub async fn discover_with_options( &self, - params: InstructionsGetDiscoveryPathsRequest, - ) -> Result { + params: InstructionsDiscoverOptions, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call( - rpc_methods::INSTRUCTIONS_GETDISCOVERYPATHS, - Some(wire_params), - ) + .call(rpc_methods::INSTRUCTIONS_DISCOVER, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `llmInference.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcLlmInference<'a> { - pub(crate) client: &'a Client, -} -impl<'a> ClientRpcLlmInference<'a> { - /// Registers an SDK client as the LLM inference callback provider. + /// 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: `llmInference.setProvider`. + /// Wire method: `instructions.discoverRepoSources`. + /// + /// # Parameters + /// + /// * `params` - Explicit discovery arguments for one location, supplied by the caller rather than derived from a session. /// /// # Returns /// - /// Indicates whether the calling client was registered as the LLM inference provider. + /// Instruction sources discovered across user, repository, and plugin sources. /// ///
/// @@ -1913,26 +2322,32 @@ impl<'a> ClientRpcLlmInference<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_provider(&self) -> Result { - let wire_params = serde_json::json!({}); + 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::LLMINFERENCE_SETPROVIDER, Some(wire_params)) + .call( + rpc_methods::INSTRUCTIONS_DISCOVERREPOSOURCES, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Delivers the response head (status + headers) for an in-flight request, correlated by the requestId the runtime supplied in httpRequestStart. Must be called exactly once per request before any httpResponseChunk frames. + /// 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: `llmInference.httpResponseStart`. + /// Wire method: `instructions.getDiscoveryPaths`. /// /// # Parameters /// - /// * `params` - Response head. + /// * `params` - Optional project paths to include when enumerating instruction discovery targets. /// /// # Returns /// - /// Whether the start frame was accepted. + /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. /// ///
/// @@ -1941,32 +2356,32 @@ impl<'a> ClientRpcLlmInference<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn http_response_start( + pub async fn get_discovery_paths( &self, - params: LlmInferenceHttpResponseStartRequest, - ) -> Result { + params: InstructionsGetDiscoveryPathsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::LLMINFERENCE_HTTPRESPONSESTART, + rpc_methods::INSTRUCTIONS_GETDISCOVERYPATHS, Some(wire_params), ) .await?; Ok(serde_json::from_value(_value)?) } - /// Delivers a body byte range (or a terminal transport error) for an in-flight response, correlated by requestId. Set `end` true on the last chunk. When `error` is set the response terminates with a transport-level failure and the runtime raises an APIConnectionError. + /// 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: `llmInference.httpResponseChunk`. + /// Wire method: `instructions.getDiscoveryPaths`. /// /// # Parameters /// - /// * `params` - A response body chunk or terminal error. + /// * `params` - Optional project paths to include when enumerating instruction discovery targets. /// /// # Returns /// - /// Whether the chunk was accepted. + /// Canonical files and directories where custom instructions can be created so the runtime will recognize them. /// ///
/// @@ -1975,43 +2390,26 @@ impl<'a> ClientRpcLlmInference<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn http_response_chunk( + /// + /// Accepts [`InstructionsGetDiscoveryPathsOptions`], including inputs added after [`InstructionsGetDiscoveryPathsRequest`]. + pub async fn get_discovery_paths_with_options( &self, - params: LlmInferenceHttpResponseChunkRequest, - ) -> Result { + params: InstructionsGetDiscoveryPathsOptions, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::LLMINFERENCE_HTTPRESPONSECHUNK, + rpc_methods::INSTRUCTIONS_GETDISCOVERYPATHS, Some(wire_params), ) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `managedSettings.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcManagedSettings<'a> { - pub(crate) client: &'a Client, -} - -impl<'a> ClientRpcManagedSettings<'a> { - /// `managedSettings.permissions.*` sub-namespace. - pub fn permissions(&self) -> ClientRpcManagedSettingsPermissions<'a> { - ClientRpcManagedSettingsPermissions { - client: self.client, - } - } - /// 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. - /// - /// Wire method: `managedSettings.read`. - /// - /// # Returns + /// 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. /// - /// Validated device-managed settings discovered before a session exists. + /// Wire method: `instructions.clearCache`. /// ///
/// @@ -2020,42 +2418,58 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn read(&self) -> Result { + pub(crate) async fn clear_cache(&self) -> Result<(), Error> { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_READ, Some(wire_params)) + .call(rpc_methods::INSTRUCTIONS_CLEARCACHE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } +} - /// 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. - /// - /// Wire method: `managedSettings.clearCache`. - /// - ///
+/// `llmInference.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcLlmInference<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcLlmInference<'a> { + /// Registers an SDK client as the LLM inference callback provider. + /// + /// Wire method: `llmInference.setProvider`. + /// + /// # Returns + /// + /// Indicates whether the calling client was registered as the LLM inference 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 clear_cache(&self) -> Result<(), Error> { + pub async fn set_provider(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_CLEARCACHE, Some(wire_params)) + .call(rpc_methods::LLMINFERENCE_SETPROVIDER, Some(wire_params)) .await?; - Ok(()) + 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. + /// Delivers the response head (status + headers) for an in-flight request, correlated by the requestId the runtime supplied in httpRequestStart. Must be called exactly once per request before any httpResponseChunk frames. /// - /// Wire method: `managedSettings.resolve`. + /// Wire method: `llmInference.httpResponseStart`. + /// + /// # Parameters + /// + /// * `params` - Response head. /// /// # Returns /// - /// Effective enterprise managed settings for an account, resolved without a session. + /// Whether the start frame was accepted. /// ///
/// @@ -2064,26 +2478,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 async fn http_response_start( + &self, + params: LlmInferenceHttpResponseStartRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, Some(wire_params)) + .call( + rpc_methods::LLMINFERENCE_HTTPRESPONSESTART, + 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. + /// Delivers a body byte range (or a terminal transport error) for an in-flight response, correlated by requestId. Set `end` true on the last chunk. When `error` is set the response terminates with a transport-level failure and the runtime raises an APIConnectionError. /// - /// Wire method: `managedSettings.resolve`. + /// Wire method: `llmInference.httpResponseChunk`. /// /// # Parameters /// - /// * `params` - Optional opaque account selection or GitHub token whose managed settings are resolved. + /// * `params` - A response body chunk or terminal error. /// /// # Returns /// - /// Effective enterprise managed settings for an account, resolved without a session. + /// Whether the chunk was accepted. /// ///
/// @@ -2092,25 +2512,43 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn resolve_with_params( + pub async fn http_response_chunk( &self, - params: ManagedSettingsResolveRequest, - ) -> Result { + params: LlmInferenceHttpResponseChunkRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, Some(wire_params)) + .call( + rpc_methods::LLMINFERENCE_HTTPRESPONSECHUNK, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } +} + +/// `managedSettings.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcManagedSettings<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcManagedSettings<'a> { + /// `managedSettings.permissions.*` sub-namespace. + pub fn permissions(&self) -> ClientRpcManagedSettingsPermissions<'a> { + ClientRpcManagedSettingsPermissions { + client: self.client, + } + } - /// 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. + /// 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.schema`. + /// Wire method: `managedSettings.readDevice`. /// /// # Returns /// - /// The authoring JSON schema for managed settings recognized by this runtime. + /// The device-managed settings layer, with the sandbox floor and the load outcome a host needs to compose it into an effective policy. /// ///
/// @@ -2119,26 +2557,22 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn schema(&self) -> Result { + pub(crate) async fn read_device(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_SCHEMA, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_READDEVICE, 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. + /// 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.validate`. + /// Wire method: `managedSettings.retainPolicy`. /// /// # Parameters /// - /// * `params` - A candidate managed-settings document to validate without applying it. - /// - /// # Returns - /// - /// Result of validating a managed-settings document. + /// * `params` - The managed layers to retain as this process's startup policy, with the outcome of fetching each one. /// ///
/// @@ -2147,29 +2581,29 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn validate( + pub(crate) async fn retain_policy( &self, - params: ManagedSettingsValidateRequest, - ) -> Result { + params: ManagedSettingsRetainPolicyParams, + ) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MANAGEDSETTINGS_VALIDATE, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_RETAINPOLICY, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// 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. + /// 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.compose`. + /// Wire method: `managedSettings.applyControls`. /// /// # Parameters /// - /// * `params` - Candidate managed-settings documents to merge without applying them. + /// * `params` - A user settings document and the effective managed policy whose controlled entries should be stamped onto it. /// /// # Returns /// - /// The effective managed settings the runtime would enforce for the given documents, in the same shape `managedSettings.resolve` returns. + /// The user settings with the managed layer's controlled entries stamped on. /// ///
/// @@ -2178,37 +2612,32 @@ impl<'a> ClientRpcManagedSettings<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn compose( + pub(crate) async fn apply_controls( &self, - params: ManagedSettingsComposeRequest, - ) -> Result { + params: ManagedSettingsApplyControlsParams, + ) -> 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_APPLYCONTROLS, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `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. + /// 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.permissions.evaluate`. + /// Wire method: `managedSettings.parseClientValue`. /// /// # Parameters /// - /// * `params` - Managed policy context and ordered operations for pure, sessionless evaluation. + /// * `params` - The client-supplied managed settings to validate and normalize. Omitting them is valid and yields no policy. /// /// # Returns /// - /// Ordered managed permission verdicts and the supplied policy's fail-closed posture. + /// The accepted managed settings, reduced to the keys a client is allowed to set. /// ///
/// @@ -2217,61 +2646,32 @@ impl<'a> ClientRpcManagedSettingsPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn evaluate( + pub(crate) async fn parse_client_value( &self, - params: ManagedPermissionsEvaluateRequest, - ) -> Result { + params: ManagedSettingsParseClientValueParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client .call( - rpc_methods::MANAGEDSETTINGS_PERMISSIONS_EVALUATE, + rpc_methods::MANAGEDSETTINGS_PARSECLIENTVALUE, 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, - } - } - - /// `mcp.registry.*` sub-namespace. - pub fn registry(&self) -> ClientRpcMcpRegistry<'a> { - ClientRpcMcpRegistry { - client: self.client, - } - } - /// Discovers MCP servers from user, workspace, plugin, and builtin sources. + /// 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: `mcp.discover`. + /// Wire method: `managedSettings.resolveEffectiveValue`. /// /// # Parameters /// - /// * `params` - Optional working directory used as context for MCP server discovery. + /// * `params` - The managed-settings layers to compose into an effective policy. Every layer is optional; omitting all of them yields no effective policy. /// /// # Returns /// - /// MCP servers discovered from user, workspace, plugin, and built-in sources. + /// The effective managed policy composed from the supplied layers. /// ///
/// @@ -2280,26 +2680,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_effective_value( + &self, + params: ManagedSettingsResolveEffectiveValueParams, + ) -> 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_RESOLVEEFFECTIVEVALUE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// 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. + /// 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: `mcp.shouldExcludeGitHubTools`. + /// Wire method: `managedSettings.resolveBypassDisableTransition`. /// /// # Parameters /// - /// * `params` - The session tool filters that decide whether the session still has a shell to run `gh` with. + /// * `params` - The bypass-permissions state a host holds today, the account now signed in, and what the managed-policy resolution said. /// /// # Returns /// - /// Whether the gh-replaceable GitHub MCP tools may be clipped for the session described by the request. + /// The bypass-permissions state a host should hold after the resolution, and what it must do to get there. /// ///
/// @@ -2308,29 +2714,32 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn should_exclude_git_hub_tools( + pub(crate) async fn resolve_bypass_disable_transition( &self, - params: McpShouldExcludeGitHubToolsRequest, - ) -> Result { + params: ManagedSettingsResolveBypassDisableTransitionParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_SHOULDEXCLUDEGITHUBTOOLS, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_RESOLVEBYPASSDISABLETRANSITION, + 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 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: `mcp.planInstall`. + /// Wire method: `managedSettings.ingest`. /// /// # 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 an ingestion should resolve managed policy for. /// /// # Returns /// - /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. + /// 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. /// ///
/// @@ -2339,29 +2748,29 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn plan_install( + pub(crate) async fn ingest( &self, - params: McpPlanInstallRequest, - ) -> Result { + params: ManagedSettingsIngestParams, + ) -> 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_INGEST, 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 (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.planInstall`. + /// Wire method: `managedSettings.resolveStartupPolicy`. /// /// # 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 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 /// - /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. + /// 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. /// ///
/// @@ -2370,31 +2779,32 @@ 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_startup_policy( &self, - params: McpPlanInstallOptions, - ) -> Result { + params: ManagedSettingsResolveStartupPolicyParams, + ) -> 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_RESOLVESTARTUPPOLICY, + 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. + /// 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.prepareInstall`. + /// Wire method: `managedSettings.loadRemoteControl`. /// /// # Parameters /// - /// * `params` - Side-effect-free preparation of one original bound remote MCP choice. + /// * `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 /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// 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. /// ///
/// @@ -2403,29 +2813,32 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn prepare_install( + pub(crate) async fn load_remote_control( &self, - params: McpPrepareInstallRequest, - ) -> Result { + params: ManagedSettingsLoadRemoteControlParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_PREPAREINSTALL, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_LOADREMOTECONTROL, + 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. + /// 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.applyInstall`. + /// Wire method: `managedSettings.resolveDeviceForceLoginOrgsGate`. /// /// # Parameters /// - /// * `params` - Applies exactly one previously prepared operation on its original connection. + /// * `params` - The credential and environment to evaluate the device-channel sign-in pin against. /// /// # Returns /// - /// An installation result together with the exact honoured contract, or a negotiation refusal. + /// 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. /// ///
/// @@ -2434,29 +2847,28 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn apply_install( + pub(crate) async fn resolve_device_force_login_orgs_gate( &self, - params: McpApplyInstallRequest, - ) -> Result { + params: ManagedSettingsResolveDeviceForceLoginOrgsGateParams, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_APPLYINSTALL, Some(wire_params)) + .call( + rpc_methods::MANAGEDSETTINGS_RESOLVEDEVICEFORCELOGINORGSGATE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// 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 + /// 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` - Read-only preparation of one owned removal under fresh selected-session authority. + /// 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. /// ///
/// @@ -2465,29 +2877,18 @@ 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 read(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_PLANUNINSTALL, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_READ, 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. - /// - /// Wire method: `mcp.applyUninstall`. - /// - /// # Parameters - /// - /// * `params` - One-use application of the exact retained removal plan. - /// - /// # 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`. /// ///
/// @@ -2496,33 +2897,2134 @@ impl<'a> ClientRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn apply_uninstall( - &self, - params: McpApplyUninstallRequest, - ) -> 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_APPLYUNINSTALL, Some(wire_params)) + .call(rpc_methods::MANAGEDSETTINGS_CLEARCACHE, 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. + /// + /// Wire method: `managedSettings.resolve`. + /// + /// # Returns + /// + /// Effective enterprise managed settings for an account, resolved without 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 resolve(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, 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. + /// + /// Wire method: `managedSettings.resolve`. + /// + /// # Parameters + /// + /// * `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. + /// + ///
+ /// + /// **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 resolve_with_params( + &self, + params: ManagedSettingsResolveRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_RESOLVE, 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. + /// + /// Wire method: `managedSettings.schema`. + /// + /// # Returns + /// + /// The authoring JSON schema for managed settings recognized by this runtime. + /// + ///
+ /// + /// **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 schema(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_SCHEMA, 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. + /// + /// Wire method: `managedSettings.validate`. + /// + /// # Parameters + /// + /// * `params` - A candidate managed-settings document to validate without applying it. + /// + /// # Returns + /// + /// Result of validating a managed-settings document. + /// + ///
+ /// + /// **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 validate( + &self, + params: ManagedSettingsValidateRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_VALIDATE, 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. + /// + /// Wire method: `managedSettings.compose`. + /// + /// # Parameters + /// + /// * `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. + /// + ///
+ /// + /// **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 compose( + &self, + params: ManagedSettingsComposeRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_COMPOSE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `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: `managedSettings.permissions.evaluate`. + /// + /// # Parameters + /// + /// * `params` - Managed policy context and ordered operations for pure, sessionless evaluation. + /// + /// # Returns + /// + /// Ordered managed permission verdicts and the supplied policy's fail-closed posture. + /// + ///
+ /// + /// **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 evaluate( + &self, + params: ManagedPermissionsEvaluateRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::MANAGEDSETTINGS_PERMISSIONS_EVALUATE, + 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, + } + } + + /// `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.discover`. + /// + /// # Parameters + /// + /// * `params` - Optional working directory used as context for MCP server discovery. + /// + /// # Returns + /// + /// MCP servers discovered from user, workspace, plugin, and built-in 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 async fn discover(&self, params: McpDiscoverRequest) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// 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.listBuiltinServerNames`. + /// + /// # Returns + /// + /// Names of the MCP servers the runtime supplies itself. + /// + ///
+ /// + /// **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_builtin_server_names( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MCP_LISTBUILTINSERVERNAMES, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// 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.shouldExcludeGitHubTools`. + /// + /// # Parameters + /// + /// * `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. + /// + ///
+ /// + /// **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 should_exclude_git_hub_tools( + &self, + params: McpShouldExcludeGitHubToolsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_SHOULDEXCLUDEGITHUBTOOLS, 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. + /// + /// Wire method: `mcp.planInstall`. + /// + /// # 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. + /// + /// # Returns + /// + /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. + /// + ///
+ /// + /// **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 plan_install( + &self, + params: McpPlanInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_PLANINSTALL, 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. + /// + /// Wire method: `mcp.planInstall`. + /// + /// # 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. + /// + /// # Returns + /// + /// Outcome of an mcp.planInstall call: either a normalised plan, or one typed refusal. Nothing is written in either case. + /// + ///
+ /// + /// **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 [`McpPlanInstallOptions`], including inputs added after [`McpPlanInstallRequest`]. + pub async fn plan_install_with_options( + &self, + params: McpPlanInstallOptions, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_PLANINSTALL, 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 + /// + /// * `params` - Side-effect-free preparation of one original bound remote MCP choice. + /// + /// # Returns + /// + /// Management result with contract receipt, or a typed request/negotiation refusal. + /// + ///
+ /// + /// **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 prepare_install( + &self, + params: McpPrepareInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_PREPAREINSTALL, 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 + /// + /// An installation result together with the exact honoured contract, or a negotiation refusal. + /// + ///
+ /// + /// **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 apply_install( + &self, + params: McpApplyInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_APPLYINSTALL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// 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 + /// + /// * `params` - Read-only preparation of one owned removal under fresh selected-session authority. + /// + /// # Returns + /// + /// Management result with contract receipt, or a typed request/negotiation refusal. + /// + ///
+ /// + /// **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 plan_uninstall( + &self, + params: McpPlanUninstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_PLANUNINSTALL, 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. + /// + /// Wire method: `mcp.applyUninstall`. + /// + /// # Parameters + /// + /// * `params` - One-use application of the exact retained removal plan. + /// + /// # Returns + /// + /// An installation result together with the exact honoured contract, or a negotiation refusal. + /// + ///
+ /// + /// **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 apply_uninstall( + &self, + params: McpApplyUninstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_APPLYUNINSTALL, 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. + /// + /// Wire method: `mcp.config.list`. + /// + /// # Returns + /// + /// User-configured MCP servers, keyed by server 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 async fn list(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MCP_CONFIG_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Adds an MCP server to user configuration. + /// + /// Wire method: `mcp.config.add`. + /// + /// # Parameters + /// + /// * `params` - MCP server name and configuration to add to user configuration. + /// + ///
+ /// + /// **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(&self, params: McpConfigAddRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_CONFIG_ADD, Some(wire_params)) + .await?; + Ok(()) + } + + /// Updates an MCP server in user configuration. + /// + /// Wire method: `mcp.config.update`. + /// + /// # Parameters + /// + /// * `params` - MCP server name and replacement configuration to write to user configuration. + /// + ///
+ /// + /// **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 update(&self, params: McpConfigUpdateRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_CONFIG_UPDATE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Removes an MCP server from user configuration. + /// + /// Wire method: `mcp.config.remove`. + /// + /// # Parameters + /// + /// * `params` - MCP server name to remove from user configuration. + /// + ///
+ /// + /// **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, params: McpConfigRemoveRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_CONFIG_REMOVE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Enables MCP servers in user configuration for new sessions. + /// + /// Wire method: `mcp.config.enable`. + /// + /// # Parameters + /// + /// * `params` - MCP server names to enable for new 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 enable(&self, params: McpConfigEnableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_CONFIG_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Disables MCP servers in user configuration for new sessions. + /// + /// Wire method: `mcp.config.disable`. + /// + /// # Parameters + /// + /// * `params` - MCP server names to disable for new 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 disable(&self, params: McpConfigDisableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_CONFIG_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Drops this runtime process's in-memory MCP server-definition cache so the next MCP config read observes disk. + /// + /// Wire method: `mcp.config.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 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. + /// + /// # Returns + /// + /// Management result with contract receipt, or a typed request/negotiation refusal. + /// + ///
+ /// + /// **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, + params: McpInstallationsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_INSTALLATIONS_LIST, 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. + /// + /// Wire method: `mcp.installations.recover`. + /// + /// # Parameters + /// + /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. + /// + /// # Returns + /// + /// Management result with contract receipt, or a typed request/negotiation refusal. + /// + ///
+ /// + /// **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 recover( + &self, + params: McpInstallationsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_INSTALLATIONS_RECOVER, 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. + /// + /// Wire method: `mcp.installations.status`. + /// + /// # Parameters + /// + /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// + /// # Returns + /// + /// Management result with contract receipt, or a typed request/negotiation refusal. + /// + ///
+ /// + /// **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 status( + &self, + params: McpInstallationOperationRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_INSTALLATIONS_STATUS, 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. + /// + /// Wire method: `mcp.installations.cancel`. + /// + /// # Parameters + /// + /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// + /// # Returns + /// + /// Management result with contract receipt, or a typed request/negotiation refusal. + /// + ///
+ /// + /// **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 cancel( + &self, + params: McpInstallationOperationRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_INSTALLATIONS_CANCEL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `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: `mcp.registry.allocateRequestId`. + /// + /// # Returns + /// + /// Request id naming a cancellable registry search. + /// + ///
+ /// + /// **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 allocate_request_id(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::MCP_REGISTRY_ALLOCATEREQUESTID, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// 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: `mcp.registry.search`. + /// + /// # Parameters + /// + /// * `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. + /// + ///
+ /// + /// **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 search( + &self, + params: McpRegistrySearchRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_REGISTRY_SEARCH, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// 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: `mcp.registry.cancel`. + /// + /// # Parameters + /// + /// * `params` - Registry search to abandon. + /// + /// # Returns + /// + /// Whether the cancel reached a live search. + /// + ///
+ /// + /// **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 cancel( + &self, + params: McpRegistryCancelRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MCP_REGISTRY_CANCEL, 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. + /// + /// Wire method: `models.list`. + /// + /// # Returns + /// + /// List of Copilot models available to the resolved user, including capabilities and billing 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 list(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MODELS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Lists Copilot models available to the authenticated user. + /// + /// Wire method: `models.list`. + /// + /// # Parameters + /// + /// * `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. + /// + ///
+ /// + /// **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: ModelsListRequest) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::MODELS_LIST, 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. + /// + /// Wire method: `models.getBuiltInCatalog`. + /// + /// # Returns + /// + /// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in 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 get_built_in_catalog(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .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, + } + } + + /// `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: `permissions.evaluateManagedPolicy`. + /// + /// # Parameters + /// + /// * `params` - The verbatim managed `permissions` slice to interpret. + /// + /// # Returns + /// + /// The policy questions a host asks before granting elevated permissions. + /// + ///
+ /// + /// **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 evaluate_managed_policy( + &self, + params: PermissionsEvaluateManagedPolicyParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::PERMISSIONS_EVALUATEMANAGEDPOLICY, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `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: `permissions.folderTrust.checkTrust`. + /// + /// # Parameters + /// + /// * `params` - A folder to check against a persisted trusted-folders list, and the configuration directory holding that list. + /// + /// # Returns + /// + /// Folder trust check 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(crate) async fn check_trust( + &self, + params: FolderTrustIsTrustedParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::PERMISSIONS_FOLDERTRUST_CHECKTRUST, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `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: `permissions.locations.cleanupOrphaned`. + /// + /// # Returns + /// + /// Which stored location permissions were dropped because their directory is gone. + /// + ///
+ /// + /// **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 cleanup_orphaned(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::PERMISSIONS_LOCATIONS_CLEANUPORPHANED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `persistence.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPersistence<'a> { + pub(crate) client: &'a Client, +} + +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: `persistence.writeRawJsonLocked`. + /// + /// # Parameters + /// + /// * `params` - A complete JSON snapshot to write under the runtime's per-path file lock. + /// + /// # Returns + /// + /// The outcome of a locked snapshot write. + /// + ///
+ /// + /// **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_raw_json_locked( + &self, + params: PersistenceWriteRawJsonLockedRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::PERSISTENCE_WRITERAWJSONLOCKED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `plugins.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcPlugins<'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. + /// + /// Wire method: `plugins.list`. + /// + /// # Returns + /// + /// Plugins installed in user/global state. + /// + ///
+ /// + /// **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::PLUGINS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Installs a plugin from a marketplace, GitHub repo, URL, or local path. + /// + /// Wire method: `plugins.install`. + /// + /// # Parameters + /// + /// * `params` - Plugin source and optional working directory for relative-path resolution. + /// + /// # Returns + /// + /// Result of installing a plugin. + /// + ///
+ /// + /// **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 install( + &self, + params: PluginsInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_INSTALL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Uninstalls an installed plugin. + /// + /// Wire method: `plugins.uninstall`. + /// + /// # Parameters + /// + /// * `params` - Name (or spec) of the plugin to uninstall. + /// + ///
+ /// + /// **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 uninstall(&self, params: PluginsUninstallRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_UNINSTALL, Some(wire_params)) + .await?; + Ok(()) + } + + /// Updates an installed plugin to its latest published version. + /// + /// Wire method: `plugins.update`. + /// + /// # Parameters + /// + /// * `params` - Name (or spec) of the plugin to update. + /// + /// # Returns + /// + /// Result of updating a single plugin. + /// + ///
+ /// + /// **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 update(&self, params: PluginsUpdateRequest) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_UPDATE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Updates every installed plugin to its latest published version. + /// + /// Wire method: `plugins.updateAll`. + /// + /// # Returns + /// + /// Result of updating all installed plugins. + /// + ///
+ /// + /// **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 update_all(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::PLUGINS_UPDATEALL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Enables installed plugins for new sessions. + /// + /// Wire method: `plugins.enable`. + /// + /// # Parameters + /// + /// * `params` - Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. + /// + ///
+ /// + /// **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 enable(&self, params: PluginsEnableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Disables installed plugins for new sessions. + /// + /// Wire method: `plugins.disable`. + /// + /// # Parameters + /// + /// * `params` - Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. + /// + ///
+ /// + /// **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 disable(&self, params: PluginsDisableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } +} + +/// `plugins.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: `plugins.builtin.set`. + /// + /// # 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`. + /// + /// # Returns + /// + /// All registered marketplaces, including built-in defaults. + /// + ///
+ /// + /// **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::PLUGINS_MARKETPLACES_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Registers a new marketplace from a source (owner/repo, URL, or local path). + /// + /// Wire method: `plugins.marketplaces.add`. + /// + /// # Parameters + /// + /// * `params` - Marketplace source and optional working directory for relative-path resolution. + /// + /// # Returns + /// + /// Result of registering a new 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 add( + &self, + params: PluginsMarketplacesAddRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_ADD, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Removes a previously-registered marketplace. When the marketplace has dependent plugins and `force` is not set, the marketplace is left intact and the result lists the dependents so the caller can decide whether to retry with `force=true`. + /// + /// Wire method: `plugins.marketplaces.remove`. + /// + /// # Parameters + /// + /// * `params` - Name of the marketplace to remove and an optional force flag. + /// + /// # Returns + /// + /// Outcome of the remove attempt, including dependent-plugin info when applicable. + /// + ///
+ /// + /// **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, + params: PluginsMarketplacesRemoveRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_REMOVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Lists plugins advertised by a registered marketplace. + /// + /// Wire method: `plugins.marketplaces.browse`. + /// + /// # 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)?) } } -/// `mcp.config.*` RPCs. +/// `sandbox.proxyCa.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcMcpConfig<'a> { +pub struct ClientRpcSandboxProxyCa<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcMcpConfig<'a> { - /// Lists MCP servers from user configuration. +impl<'a> ClientRpcSandboxProxyCa<'a> { + /// Reports whether this runtime process can offer installing the sandbox credential-proxy certificate authority into OS trust. /// - /// Wire method: `mcp.config.list`. + /// Wire method: `sandbox.proxyCa.checkInstallationSupport`. /// /// # Returns /// - /// User-configured MCP servers, keyed by server name. + /// True when this process can offer installing the sandbox credential-proxy certificate authority into OS trust. /// ///
/// @@ -2531,22 +5033,31 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { + pub async fn check_installation_support( + &self, + ) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_CONFIG_LIST, Some(wire_params)) + .call( + rpc_methods::SANDBOX_PROXYCA_CHECKINSTALLATIONSUPPORT, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Adds an MCP server to user configuration. + /// Reports whether this sandbox configuration uses the credential proxy and can need certificate-authority setup. /// - /// Wire method: `mcp.config.add`. + /// Wire method: `sandbox.proxyCa.checkRelevance`. /// /// # Parameters /// - /// * `params` - MCP server name and configuration to add to user configuration. + /// * `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. /// ///
/// @@ -2555,22 +5066,32 @@ 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 check_relevance( + &self, + params: SandboxProxyCaRequest, + ) -> 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::SANDBOX_PROXYCA_CHECKRELEVANCE, + Some(wire_params), + ) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Updates an MCP server in user configuration. + /// 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: `mcp.config.update`. + /// Wire method: `sandbox.proxyCa.getStatus`. /// /// # Parameters /// - /// * `params` - MCP server name and replacement configuration to write to user configuration. + /// * `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. /// ///
/// @@ -2579,22 +5100,29 @@ 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 get_status( + &self, + params: SandboxProxyCaRequest, + ) -> 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::SANDBOX_PROXYCA_GETSTATUS, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Removes an MCP server from user configuration. + /// 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: `mcp.config.remove`. + /// Wire method: `sandbox.proxyCa.create`. /// /// # Parameters /// - /// * `params` - MCP server name to remove from user configuration. + /// * `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. /// ///
/// @@ -2603,22 +5131,29 @@ 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 create( + &self, + params: SandboxProxyCaRequest, + ) -> 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::SANDBOX_PROXYCA_CREATE, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Enables MCP servers in user configuration for new sessions. + /// 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: `mcp.config.enable`. + /// Wire method: `sandbox.proxyCa.rotate`. /// /// # Parameters /// - /// * `params` - MCP server names to enable for new sessions. + /// * `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. /// ///
/// @@ -2627,22 +5162,29 @@ 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 rotate( + &self, + params: SandboxProxyCaRequest, + ) -> 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::SANDBOX_PROXYCA_ROTATE, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Disables MCP servers in user configuration for new sessions. + /// 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: `mcp.config.disable`. + /// Wire method: `sandbox.proxyCa.trust`. /// /// # Parameters /// - /// * `params` - MCP server names to disable for new sessions. + /// * `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. /// ///
/// @@ -2651,18 +5193,25 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn disable(&self, params: McpConfigDisableRequest) -> Result<(), Error> { + pub async fn trust( + &self, + params: SandboxProxyCaRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_CONFIG_DISABLE, Some(wire_params)) + .call(rpc_methods::SANDBOX_PROXYCA_TRUST, 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. + /// 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: `mcp.config.reload`. + /// Wire method: `sandbox.proxyCa.remove`. + /// + /// # Returns + /// + /// Status of the persistent certificate authority of the sandbox credential proxy. /// ///
/// @@ -2671,34 +5220,30 @@ impl<'a> ClientRpcMcpConfig<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn reload(&self) -> Result<(), Error> { + pub async fn remove(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_CONFIG_RELOAD, Some(wire_params)) + .call(rpc_methods::SANDBOX_PROXYCA_REMOVE, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } } -/// `mcp.installations.*` RPCs. +/// `sandbox.proxySecret.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcMcpInstallations<'a> { +pub struct ClientRpcSandboxProxySecret<'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 +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. /// - /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. + /// Wire method: `sandbox.proxySecret.getLocation`. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Where this runtime keeps the sandbox proxy password. /// ///
/// @@ -2707,29 +5252,25 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list( - &self, - params: McpInstallationsRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub(crate) async fn get_location(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_LIST, Some(wire_params)) + .call( + rpc_methods::SANDBOX_PROXYSECRET_GETLOCATION, + 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. - /// - /// Wire method: `mcp.installations.recover`. - /// - /// # Parameters + /// 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. /// - /// * `params` - New-work inventory or recovery request under an explicitly selected existing session. + /// Wire method: `sandbox.proxySecret.get`. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// The stored sandbox proxy password, if one is present. /// ///
/// @@ -2738,29 +5279,26 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn recover( - &self, - params: McpInstallationsRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub(crate) async fn get(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_RECOVER, Some(wire_params)) + .call(rpc_methods::SANDBOX_PROXYSECRET_GET, 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. + /// 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: `mcp.installations.status`. + /// Wire method: `sandbox.proxySecret.save`. /// /// # Parameters /// - /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. + /// * `params` - The sandbox proxy password to store. /// /// # Returns /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Where the sandbox proxy password was stored. /// ///
/// @@ -2769,29 +5307,21 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn status( + pub(crate) async fn save( &self, - params: McpInstallationOperationRequest, - ) -> Result { + params: SandboxProxySecretSaveRequest, + ) -> 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::SANDBOX_PROXYSECRET_SAVE, 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. - /// - /// Wire method: `mcp.installations.cancel`. - /// - /// # Parameters - /// - /// * `params` - Existing-operation control. A new session selector is deliberately not accepted. - /// - /// # Returns + /// 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. /// - /// Management result with contract receipt, or a typed request/negotiation refusal. + /// Wire method: `sandbox.proxySecret.delete`. /// ///
/// @@ -2800,33 +5330,34 @@ impl<'a> ClientRpcMcpInstallations<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn cancel( - &self, - params: McpInstallationOperationRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub(crate) async fn delete(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MCP_INSTALLATIONS_CANCEL, Some(wire_params)) + .call(rpc_methods::SANDBOX_PROXYSECRET_DELETE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } } -/// `mcp.registry.*` RPCs. +/// `sandbox.settings.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcMcpRegistry<'a> { +pub struct ClientRpcSandboxSettings<'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. +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: `mcp.registry.allocateRequestId`. + /// Wire method: `sandbox.settings.applyDefaults`. + /// + /// # Parameters + /// + /// * `params` - The stored sandbox settings to resolve against the defaults. /// /// # Returns /// - /// Request id naming a cancellable registry search. + /// The effective sandbox settings, with every unset value resolved to its default. /// ///
/// @@ -2835,29 +5366,40 @@ impl<'a> ClientRpcMcpRegistry<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn allocate_request_id(&self) -> Result { - let wire_params = serde_json::json!({}); + 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::MCP_REGISTRY_ALLOCATEREQUESTID, + 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, +} - /// 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. +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: `mcp.registry.search`. + /// Wire method: `secrets.addFilterValues`. /// /// # Parameters /// - /// * `params` - Registry search terms, the credential to search under, and the request id that makes the search cancellable. + /// * `params` - Secret values to add to the redaction filter. /// /// # Returns /// - /// Servers selected from the registry response. + /// Confirmation that the secret values were registered. /// ///
/// @@ -2866,29 +5408,37 @@ impl<'a> ClientRpcMcpRegistry<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn search( + pub async fn add_filter_values( &self, - params: McpRegistrySearchRequest, - ) -> Result { + params: SecretsAddFilterValuesRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_REGISTRY_SEARCH, Some(wire_params)) + .call(rpc_methods::SECRETS_ADDFILTERVALUES, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } +} - /// 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. +/// `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: `mcp.registry.cancel`. + /// Wire method: `sessionFs.setProvider`. /// /// # Parameters /// - /// * `params` - Registry search to abandon. + /// * `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 /// - /// Whether the cancel reached a live search. + /// Indicates whether the calling client was registered as the session filesystem provider. /// ///
/// @@ -2897,33 +5447,33 @@ impl<'a> ClientRpcMcpRegistry<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn cancel( + pub async fn set_provider( &self, - params: McpRegistryCancelRequest, - ) -> Result { + params: SessionFsSetProviderRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MCP_REGISTRY_CANCEL, Some(wire_params)) + .call(rpc_methods::SESSIONFS_SETPROVIDER, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } } -/// `models.*` RPCs. +/// `sessions.*` RPCs. #[derive(Clone, Copy)] -pub struct ClientRpcModels<'a> { +pub struct ClientRpcSessions<'a> { pub(crate) client: &'a Client, } -impl<'a> ClientRpcModels<'a> { - /// Lists Copilot models available to the authenticated user. +impl<'a> ClientRpcSessions<'a> { + /// Creates or resumes a local session and returns the opened session ID. /// - /// Wire method: `models.list`. + /// Wire method: `sessions.open`. /// /// # Returns /// - /// List of Copilot models available to the resolved user, including capabilities and billing metadata. + /// Result of opening a session. /// ///
/// @@ -2932,26 +5482,26 @@ impl<'a> ClientRpcModels<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { + pub async fn open(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::MODELS_LIST, Some(wire_params)) + .call(rpc_methods::SESSIONS_OPEN, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists Copilot models available to the authenticated user. + /// Creates a new session by forking persisted history from an existing session. /// - /// Wire method: `models.list`. + /// Wire method: `sessions.fork`. /// /// # Parameters /// - /// * `params` - Optional opaque account selection or compatibility GitHub token used to list models. + /// * `params` - Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. /// /// # Returns /// - /// List of Copilot models available to the resolved user, including capabilities and billing metadata. + /// Identifier and optional friendly name assigned to the newly forked session. /// ///
/// @@ -2960,22 +5510,26 @@ 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 fork(&self, params: SessionsForkRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MODELS_LIST, Some(wire_params)) + .call(rpc_methods::SESSIONS_FORK, 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. + /// Connects to an existing remote session and exposes it as an SDK session. /// - /// Wire method: `models.getBuiltInCatalog`. + /// Wire method: `sessions.connect`. + /// + /// # Parameters + /// + /// * `params` - Remote session connection parameters. /// /// # Returns /// - /// The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. + /// Remote session connection result. /// ///
/// @@ -2984,44 +5538,25 @@ 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 connect( + &self, + params: ConnectRemoteSessionParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::MODELS_GETBUILTINCATALOG, Some(wire_params)) + .call(rpc_methods::SESSIONS_CONNECT, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `plugins.*` RPCs. -#[derive(Clone, Copy)] -pub struct ClientRpcPlugins<'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. + /// 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: `plugins.list`. + /// Wire method: `sessions.list`. /// /// # Returns /// - /// Plugins installed in user/global state. + /// Sessions matching the filter, ordered most-recently-modified first. /// ///
/// @@ -3030,26 +5565,26 @@ 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::SESSIONS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Installs a plugin from a marketplace, GitHub repo, URL, or local path. + /// 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: `plugins.install`. + /// Wire method: `sessions.list`. /// /// # Parameters /// - /// * `params` - Plugin source and optional working directory for relative-path resolution. + /// * `params` - Optional source filter, metadata-load limit, and context filter applied to the returned sessions. /// /// # Returns /// - /// Result of installing a plugin. + /// Sessions matching the filter, ordered most-recently-modified first. /// ///
/// @@ -3058,25 +5593,29 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn install( + pub async fn list_with_params( &self, - params: PluginsInstallRequest, - ) -> Result { + params: SessionsListRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_INSTALL, Some(wire_params)) + .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Uninstalls an installed plugin. + /// Reads lightweight persisted metadata for one local session without opening it. /// - /// Wire method: `plugins.uninstall`. + /// Wire method: `sessions.getMetadata`. /// /// # Parameters /// - /// * `params` - Name (or spec) of the plugin to uninstall. + /// * `params` - Session ID whose persisted metadata should be read. + /// + /// # Returns + /// + /// Persisted local session metadata when the session exists. /// ///
/// @@ -3085,26 +5624,29 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn uninstall(&self, params: PluginsUninstallRequest) -> Result<(), Error> { + 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::PLUGINS_UNINSTALL, Some(wire_params)) + .call(rpc_methods::SESSIONS_GETMETADATA, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Updates an installed plugin to its latest published version. + /// 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: `plugins.update`. + /// Wire method: `sessions.getClientMetadata`. /// /// # Parameters /// - /// * `params` - Name (or spec) of the plugin to update. + /// * `params` - Bounded batch request for client-owned metadata from persisted local sessions. /// /// # Returns /// - /// Result of updating a single plugin. + /// Ordered client metadata outcomes for the requested local sessions. /// ///
/// @@ -3113,22 +5655,29 @@ 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 get_client_metadata( + &self, + params: SessionsGetClientMetadataRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_UPDATE, Some(wire_params)) + .call(rpc_methods::SESSIONS_GETCLIENTMETADATA, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Updates every installed plugin to its latest published version. + /// 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: `plugins.updateAll`. + /// Wire method: `sessions.readPersistedEvents`. + /// + /// # Parameters + /// + /// * `params` - Pagination options for reading an inactive or active local session's persisted event journal. /// /// # Returns /// - /// Result of updating all installed plugins. + /// Batch of session events returned by a read, with cursor and continuation metadata. /// ///
/// @@ -3137,22 +5686,29 @@ 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 read_persisted_events( + &self, + params: SessionsReadPersistedEventsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::PLUGINS_UPDATEALL, Some(wire_params)) + .call(rpc_methods::SESSIONS_READPERSISTEDEVENTS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Enables installed plugins for new sessions. + /// Lists recent local session IDs that contain user-visible history, omitting housekeeping-only sessions. /// - /// Wire method: `plugins.enable`. + /// Wire method: `sessions.listNonEmptySessionIds`. /// /// # Parameters /// - /// * `params` - Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. + /// * `params` - Limit for non-empty local session IDs. + /// + /// # Returns + /// + /// Recent local session IDs that contain user-visible history. /// ///
/// @@ -3161,22 +5717,32 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn enable(&self, params: PluginsEnableRequest) -> Result<(), Error> { + 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::PLUGINS_ENABLE, Some(wire_params)) + .call( + rpc_methods::SESSIONS_LISTNONEMPTYSESSIONIDS, + Some(wire_params), + ) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } - /// Disables installed plugins for new sessions. + /// Finds the local session bound to a GitHub task ID, if any. /// - /// Wire method: `plugins.disable`. + /// Wire method: `sessions.findByTaskId`. /// /// # Parameters /// - /// * `params` - Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. + /// * `params` - GitHub task ID to look up. + /// + /// # Returns + /// + /// ID of the local session bound to the given GitHub task, or omitted when none. /// ///
/// @@ -3185,30 +5751,29 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn disable(&self, params: PluginsDisableRequest) -> Result<(), Error> { + 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::PLUGINS_DISABLE, Some(wire_params)) + .call(rpc_methods::SESSIONS_FINDBYTASKID, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } -} - -/// `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. + /// Resolves a UUID prefix to a unique session ID, if exactly one session matches. /// - /// Wire method: `plugins.builtin.set`. + /// Wire method: `sessions.findByPrefix`. /// /// # Parameters /// - /// * `params` - Trusted built-in plugin directories to use for this runtime process. + /// * `params` - UUID prefix to resolve to a unique session ID. + /// + /// # Returns + /// + /// Session ID matching the prefix, omitted when no unique match exists. /// ///
/// @@ -3217,30 +5782,29 @@ impl<'a> ClientRpcPluginsBuiltin<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set(&self, params: PluginsBuiltinSetRequest) -> Result<(), Error> { + 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::PLUGINS_BUILTIN_SET, Some(wire_params)) + .call(rpc_methods::SESSIONS_FINDBYPREFIX, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } -} - -/// `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). + /// Returns the most-relevant prior session for a given working-directory context. /// - /// Wire method: `plugins.marketplaces.list`. + /// Wire method: `sessions.getLastForContext`. + /// + /// # Parameters + /// + /// * `params` - Optional working-directory context used to score session relevance. /// /// # Returns /// - /// All registered marketplaces, including built-in defaults. + /// Most-relevant session ID for the supplied context, or omitted when no sessions exist. /// ///
/// @@ -3249,26 +5813,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 get_last_for_context( + &self, + params: SessionsGetLastForContextRequest, + ) -> 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::SESSIONS_GETLASTFORCONTEXT, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Registers a new marketplace from a source (owner/repo, URL, or local path). + /// 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: `plugins.marketplaces.add`. + /// Wire method: `sessions.getEventFilePath`. /// /// # Parameters /// - /// * `params` - Marketplace source and optional working directory for relative-path resolution. + /// * `params` - Session ID whose event-log file path to compute. /// /// # Returns /// - /// Result of registering a new marketplace. + /// Absolute path to the session's events.jsonl file on disk. /// ///
/// @@ -3277,29 +5844,25 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn add( + pub(crate) async fn get_event_file_path( &self, - params: PluginsMarketplacesAddRequest, - ) -> Result { + params: SessionsGetEventFilePathRequest, + ) -> 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::SESSIONS_GETEVENTFILEPATH, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Removes a previously-registered marketplace. When the marketplace has dependent plugins and `force` is not set, the marketplace is left intact and the result lists the dependents so the caller can decide whether to retry with `force=true`. - /// - /// Wire method: `plugins.marketplaces.remove`. - /// - /// # Parameters + /// Returns the on-disk byte size of each session's workspace directory. /// - /// * `params` - Name of the marketplace to remove and an optional force flag. + /// Wire method: `sessions.getSizes`. /// /// # Returns /// - /// Outcome of the remove attempt, including dependent-plugin info when applicable. + /// Map of sessionId -> on-disk size in bytes for each session's workspace directory. /// ///
/// @@ -3308,29 +5871,26 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn remove( - &self, - params: PluginsMarketplacesRemoveRequest, - ) -> Result { - let wire_params = serde_json::to_value(params)?; + pub async fn get_sizes(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::PLUGINS_MARKETPLACES_REMOVE, Some(wire_params)) + .call(rpc_methods::SESSIONS_GETSIZES, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists plugins advertised by a registered marketplace. + /// Returns the subset of the supplied session IDs that are currently held by another running process. /// - /// Wire method: `plugins.marketplaces.browse`. + /// Wire method: `sessions.checkInUse`. /// /// # Parameters /// - /// * `params` - Name of the marketplace whose plugin catalog to fetch. + /// * `params` - Session IDs to test for live in-use locks. /// /// # Returns /// - /// Plugins advertised by the marketplace. + /// Session IDs from the input set that are currently in use by another process. /// ///
/// @@ -3339,25 +5899,29 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn browse( + pub async fn check_in_use( &self, - params: PluginsMarketplacesBrowseRequest, - ) -> Result { + params: SessionsCheckInUseRequest, + ) -> 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::SESSIONS_CHECKINUSE, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Re-fetches one or all registered marketplace catalogs. + /// 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: `plugins.marketplaces.refresh`. + /// Wire method: `sessions.getPersistedRemoteSteerable`. + /// + /// # Parameters + /// + /// * `params` - Session ID to look up the persisted remote-steerable flag for. /// /// # Returns /// - /// Result of refreshing one or more marketplace catalogs. + /// The session's persisted remote-steerable flag, or omitted when no value has been persisted. /// ///
/// @@ -3366,26 +5930,32 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn refresh(&self) -> Result { - let wire_params = serde_json::json!({}); + 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::PLUGINS_MARKETPLACES_REFRESH, Some(wire_params)) + .call( + rpc_methods::SESSIONS_GETPERSISTEDREMOTESTEERABLE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Re-fetches one or all registered marketplace catalogs. + /// Closes a session: emits shutdown, flushes pending events, releases the in-use lock, and disposes the active session. /// - /// Wire method: `plugins.marketplaces.refresh`. + /// Wire method: `sessions.close`. /// /// # Parameters /// - /// * `params` - Optional marketplace name; omit to refresh all. + /// * `params` - Session ID to close. /// /// # Returns /// - /// Result of refreshing one or more marketplace catalogs. + /// 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. /// ///
/// @@ -3394,29 +5964,26 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn refresh_with_params( - &self, - params: PluginsMarketplacesRefreshRequest, - ) -> Result { + pub async fn close(&self, params: SessionsCloseRequest) -> 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::SESSIONS_CLOSE, 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. + /// Closes, deactivates, and deletes a set of sessions, returning the bytes freed per session. /// - /// Wire method: `runtime.shutdown`. + /// 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. /// ///
/// @@ -3425,37 +5992,25 @@ 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 async fn bulk_delete( + &self, + params: SessionsBulkDeleteRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::RUNTIME_SHUTDOWN, Some(wire_params)) + .call(rpc_methods::SESSIONS_BULKDELETE, 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, - } + 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. + /// Deletes one local session from disk after running the same lifecycle hooks as the session manager. /// - /// Wire method: `sandbox.getHostSupport`. + /// Wire method: `sessions.delete`. /// - /// # Returns + /// # Parameters /// - /// 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. + /// * `params` - Session ID to delete from disk. /// ///
/// @@ -3464,34 +6019,26 @@ impl<'a> ClientRpcSandbox<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_host_support(&self) -> Result { - let wire_params = serde_json::json!({}); + 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::SANDBOX_GETHOSTSUPPORT, Some(wire_params)) + .call(rpc_methods::SESSIONS_DELETE, Some(wire_params)) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } -} - -/// `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. + /// Deletes sessions older than the given threshold, with optional dry-run and exclusion list. /// - /// Wire method: `sandbox.proxyCa.getStatus`. + /// Wire method: `sessions.pruneOld`. /// /// # 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` - Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag. /// ///
/// @@ -3500,29 +6047,29 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_status( + pub async fn prune_old( &self, - params: SandboxProxyCaRequest, - ) -> Result { + params: SessionsPruneOldRequest, + ) -> 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::SESSIONS_PRUNEOLD, 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. + /// Flushes a session's pending events to disk. /// - /// Wire method: `sandbox.proxyCa.create`. + /// Wire method: `sessions.save`. /// /// # 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` - Session ID whose pending events should be flushed to disk. /// /// # Returns /// - /// Result of creating the persistent certificate authority of the sandbox credential proxy. + /// Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed). /// ///
/// @@ -3531,29 +6078,26 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn create( - &self, - params: SandboxProxyCaRequest, - ) -> Result { + pub async fn save(&self, params: SessionsSaveRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SANDBOX_PROXYCA_CREATE, Some(wire_params)) + .call(rpc_methods::SESSIONS_SAVE, 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. + /// Releases the in-use lock held by this process for a session. /// - /// Wire method: `sandbox.proxyCa.rotate`. + /// Wire method: `sessions.releaseLock`. /// /// # 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` - Session ID whose in-use lock should be released. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// 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. /// ///
/// @@ -3562,29 +6106,29 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn rotate( + pub async fn release_lock( &self, - params: SandboxProxyCaRequest, - ) -> Result { + params: SessionsReleaseLockRequest, + ) -> 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::SESSIONS_RELEASELOCK, 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. + /// Backfills missing summary and context fields on the supplied session metadata records. /// - /// Wire method: `sandbox.proxyCa.trust`. + /// Wire method: `sessions.enrichMetadata`. /// /// # 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` - Session metadata records to enrich with summary and context information. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted. /// ///
/// @@ -3593,25 +6137,29 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn trust( + pub async fn enrich_metadata( &self, - params: SandboxProxyCaRequest, - ) -> Result { + params: SessionsEnrichMetadataRequest, + ) -> 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::SESSIONS_ENRICHMETADATA, 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. + /// 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 /// - /// Wire method: `sandbox.proxyCa.remove`. + /// * `params` - Identity, state location and starting context for a workspace record. /// /// # Returns /// - /// Status of the persistent certificate authority of the sandbox credential proxy. + /// The workspace record that was written. /// ///
/// @@ -3620,34 +6168,29 @@ impl<'a> ClientRpcSandboxProxyCa<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn remove(&self) -> Result { - let wire_params = serde_json::json!({}); + 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::SANDBOX_PROXYCA_REMOVE, Some(wire_params)) + .call(rpc_methods::SESSIONS_CREATEWORKSPACE, 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). + /// 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: `secrets.addFilterValues`. + /// Wire method: `sessions.loadWorkspace`. /// /// # Parameters /// - /// * `params` - Secret values to add to the redaction filter. + /// * `params` - Where the session's state lives, as a root directory and the session ID under it. /// /// # Returns /// - /// Confirmation that the secret values were registered. + /// The workspace record on disk, omitted when the session has none. /// ///
/// @@ -3656,37 +6199,29 @@ impl<'a> ClientRpcSecrets<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn add_filter_values( + pub(crate) async fn load_workspace( &self, - params: SecretsAddFilterValuesRequest, - ) -> Result { + params: SessionsLoadWorkspaceRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SECRETS_ADDFILTERVALUES, Some(wire_params)) + .call(rpc_methods::SESSIONS_LOADWORKSPACE, 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. + /// 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: `sessionFs.setProvider`. + /// Wire method: `sessions.updateWorkspaceFields`. /// /// # 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. + /// * `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 /// - /// Indicates whether the calling client was registered as the session filesystem provider. + /// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. /// ///
/// @@ -3695,33 +6230,32 @@ impl<'a> ClientRpcSessionFs<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn set_provider( + pub(crate) async fn update_workspace_fields( &self, - params: SessionFsSetProviderRequest, - ) -> Result { + params: SessionsUpdateWorkspaceFieldsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONFS_SETPROVIDER, Some(wire_params)) + .call( + rpc_methods::SESSIONS_UPDATEWORKSPACEFIELDS, + 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. + /// Reloads user, plugin, and (optionally) repo hooks on the active session. /// - /// Wire method: `sessions.open`. + /// Wire method: `sessions.reloadPluginHooks`. + /// + /// # Parameters + /// + /// * `params` - Active session ID and an optional flag for deferring repo-level hooks until folder trust. /// /// # Returns /// - /// Result of opening a session. + /// 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. /// ///
/// @@ -3730,26 +6264,29 @@ 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 reload_plugin_hooks( + &self, + params: SessionsReloadPluginHooksRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_OPEN, Some(wire_params)) + .call(rpc_methods::SESSIONS_RELOADPLUGINHOOKS, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Creates a new session by forking persisted history from an existing session. + /// Loads previously-deferred repo-level hooks on the active session, returning queued startup prompts. /// - /// Wire method: `sessions.fork`. + /// Wire method: `sessions.loadDeferredRepoHooks`. /// /// # Parameters /// - /// * `params` - Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. + /// * `params` - Active session ID whose deferred repo-level hooks should be loaded. /// /// # Returns /// - /// Identifier and optional friendly name assigned to the newly forked session. + /// Queued repo-level startup prompts and the total hook command count after loading. /// ///
/// @@ -3758,26 +6295,32 @@ 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 load_deferred_repo_hooks( + &self, + params: SessionsLoadDeferredRepoHooksRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_FORK, Some(wire_params)) + .call( + rpc_methods::SESSIONS_LOADDEFERREDREPOHOOKS, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Connects to an existing remote session and exposes it as an SDK session. + /// Replaces the manager-wide additional plugins registered with the session manager. /// - /// Wire method: `sessions.connect`. + /// Wire method: `sessions.setAdditionalPlugins`. /// /// # Parameters /// - /// * `params` - Remote session connection parameters. + /// * `params` - Manager-wide additional plugins to register; replaces any previously-configured set. /// /// # Returns /// - /// Remote session connection result. + /// 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. /// ///
/// @@ -3786,25 +6329,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn connect( + pub async fn set_additional_plugins( &self, - params: ConnectRemoteSessionParams, - ) -> Result { + params: SessionsSetAdditionalPluginsRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_CONNECT, Some(wire_params)) + .call( + rpc_methods::SESSIONS_SETADDITIONALPLUGINS, + 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.). + /// 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.list`. + /// Wire method: `sessions.getBoardEntryCount`. + /// + /// # Parameters + /// + /// * `params` - Session ID whose board entry count should be returned. /// /// # Returns /// - /// Sessions matching the filter, ordered most-recently-modified first. + /// Dynamic-context board entry count, when available. /// ///
/// @@ -3813,26 +6363,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list(&self) -> Result { - let wire_params = serde_json::json!({}); + pub(crate) async fn get_board_entry_count( + &self, + params: SessionsGetBoardEntryCountRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) + .call(rpc_methods::SESSIONS_GETBOARDENTRYCOUNT, 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.). + /// 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.list`. + /// Wire method: `sessions.startRemoteControl`. /// /// # Parameters /// - /// * `params` - Optional source filter, metadata-load limit, and context filter applied to the returned sessions. + /// * `params` - Parameters for attaching the remote-control singleton to a session. /// /// # Returns /// - /// Sessions matching the filter, ordered most-recently-modified first. + /// Wrapper for the singleton's current status. /// ///
/// @@ -3841,29 +6394,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list_with_params( + pub async fn start_remote_control( &self, - params: SessionsListRequest, - ) -> Result { + params: SessionsStartRemoteControlRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) + .call(rpc_methods::SESSIONS_STARTREMOTECONTROL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Reads lightweight persisted metadata for one local session without opening it. + /// 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.getMetadata`. + /// Wire method: `sessions.transferRemoteControl`. /// /// # Parameters /// - /// * `params` - Session ID whose persisted metadata should be read. + /// * `params` - Parameters for atomically rebinding the remote-control singleton. /// /// # Returns /// - /// Persisted local session metadata when the session exists. + /// Outcome of a transferRemoteControl call. /// ///
/// @@ -3872,29 +6425,32 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn get_metadata( + pub async fn transfer_remote_control( &self, - params: SessionsGetMetadataRequest, - ) -> Result { + params: SessionsTransferRemoteControlRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETMETADATA, Some(wire_params)) + .call( + rpc_methods::SESSIONS_TRANSFERREMOTECONTROL, + 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. + /// 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.getClientMetadata`. + /// Wire method: `sessions.setRemoteControlSteering`. /// /// # Parameters /// - /// * `params` - Bounded batch request for client-owned metadata from persisted local sessions. + /// * `params` - Patch for the singleton's steering state. /// /// # Returns /// - /// Ordered client metadata outcomes for the requested local sessions. + /// Wrapper for the singleton's current status. /// ///
/// @@ -3903,29 +6459,28 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn get_client_metadata( + pub async fn set_remote_control_steering( &self, - params: SessionsGetClientMetadataRequest, - ) -> Result { + params: SessionsSetRemoteControlSteeringRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_GETCLIENTMETADATA, Some(wire_params)) + .call( + rpc_methods::SESSIONS_SETREMOTECONTROLSTEERING, + 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 + /// 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). /// - /// * `params` - Pagination options for reading an inactive or active local session's persisted event journal. + /// Wire method: `sessions.stopRemoteControl`. /// /// # Returns /// - /// Batch of session events returned by a read, with cursor and continuation metadata. + /// Outcome of a stopRemoteControl call. /// ///
/// @@ -3934,29 +6489,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 stop_remote_control(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SESSIONS_READPERSISTEDEVENTS, Some(wire_params)) + .call(rpc_methods::SESSIONS_STOPREMOTECONTROL, Some(wire_params)) .await?; Ok(serde_json::from_value(_value)?) } - /// Lists recent local session IDs that contain user-visible history, omitting housekeeping-only sessions. + /// 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.listNonEmptySessionIds`. + /// Wire method: `sessions.stopRemoteControl`. /// /// # Parameters /// - /// * `params` - Limit for non-empty local session IDs. + /// * `params` - Parameters for stopping the remote-control singleton. /// /// # Returns /// - /// Recent local session IDs that contain user-visible history. + /// Outcome of a stopRemoteControl call. /// ///
/// @@ -3965,32 +6517,25 @@ 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 stop_remote_control_with_params( &self, - params: SessionsListNonEmptySessionIdsRequest, - ) -> Result { + params: SessionsStopRemoteControlRequest, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call( - rpc_methods::SESSIONS_LISTNONEMPTYSESSIONIDS, - Some(wire_params), - ) + .call(rpc_methods::SESSIONS_STOPREMOTECONTROL, 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 + /// Returns the current state of the remote-control singleton, including the attached session id and frontend URL when active. /// - /// * `params` - GitHub task ID to look up. + /// Wire method: `sessions.getRemoteControlStatus`. /// /// # Returns /// - /// ID of the local session bound to the given GitHub task, or omitted when none. + /// Wrapper for the singleton's current status. /// ///
/// @@ -3998,30 +6543,26 @@ impl<'a> ClientRpcSessions<'a> { /// 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)?; + ///
+ pub async fn get_remote_control_status(&self) -> Result { + let wire_params = serde_json::json!({}); let _value = self .client - .call(rpc_methods::SESSIONS_FINDBYTASKID, Some(wire_params)) + .call( + rpc_methods::SESSIONS_GETREMOTECONTROLSTATUS, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Resolves a UUID prefix to a unique session ID, if exactly one session matches. + /// 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.findByPrefix`. + /// Wire method: `sessions.configureSessionExtensions`. /// /// # Parameters /// - /// * `params` - UUID prefix to resolve to a unique session ID. - /// - /// # Returns - /// - /// Session ID matching the prefix, omitted when no unique match exists. + /// * `params` - Params to attach or detach an in-process ExtensionController delegate. /// ///
/// @@ -4030,29 +6571,54 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn find_by_prefix( + pub(crate) async fn configure_session_extensions( &self, - params: SessionsFindByPrefixRequest, - ) -> Result { + params: ConfigureSessionExtensionsParams, + ) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_FINDBYPREFIX, Some(wire_params)) + .call( + rpc_methods::SESSIONS_CONFIGURESESSIONEXTENSIONS, + Some(wire_params), + ) .await?; - Ok(serde_json::from_value(_value)?) + 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, + } } - /// Returns the most-relevant prior session for a given working-directory context. + /// `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. /// ///
/// @@ -4061,29 +6627,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. + /// Plans installation of the Skill pinned by an Agent Finder materialisation reference without writing files. The runtime retrieves the descriptor only from its fixed Agent Finder authority and applies the same verification as skills.planInstall; the review shows only verified data. Apply the returned plan with skills.applyInstall. /// - /// Wire method: `sessions.getEventFilePath`. + /// Wire method: `skills.planInstallFromReference`. /// /// # Parameters /// - /// * `params` - Session ID whose event-log file path to compute. + /// * `params` - Side-effect-free planning of one Skill from a materialisation reference rather than a search candidate. /// /// # Returns /// - /// Absolute path to the session's events.jsonl file on disk. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4092,25 +6658,32 @@ 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 plan_install_from_reference( &self, - params: SessionsGetEventFilePathRequest, - ) -> Result { + params: SkillPlanInstallFromReferenceRequest, + ) -> 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_PLANINSTALLFROMREFERENCE, + Some(wire_params), + ) .await?; Ok(serde_json::from_value(_value)?) } - /// Returns the on-disk byte size of each session's workspace directory. + /// 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.getSizes`. + /// Wire method: `skills.applyInstall`. + /// + /// # Parameters + /// + /// * `params` - Applies exactly one retained verified Skill installation plan. /// /// # Returns /// - /// Map of sessionId -> on-disk size in bytes for each session's workspace directory. + /// Skill installation result with the honoured contract, or a typed request/negotiation refusal. /// ///
/// @@ -4119,26 +6692,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 apply_install( + &self, + params: SkillApplyInstallRequest, + ) -> 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_APPLYINSTALL, 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. + /// 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.checkInUse`. + /// Wire method: `skills.planUninstall`. /// /// # Parameters /// - /// * `params` - Session IDs to test for live in-use locks. + /// * `params` - Read-only preparation of one owned Skill removal under fresh selected-session authority. /// /// # Returns /// - /// Session IDs from the input set that are currently in use by another process. + /// Skill installation management result with the honoured contract, or a typed refusal. /// ///
/// @@ -4147,29 +6723,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn check_in_use( + pub async fn plan_uninstall( &self, - params: SessionsCheckInUseRequest, - ) -> Result { + params: SkillPlanUninstallRequest, + ) -> 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_PLANUNINSTALL, 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. + /// 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.getPersistedRemoteSteerable`. + /// Wire method: `skills.applyUninstall`. /// /// # Parameters /// - /// * `params` - Session ID to look up the persisted remote-steerable flag for. + /// * `params` - One-use application of the exact retained Skill removal plan. /// /// # Returns /// - /// The session's persisted remote-steerable flag, or omitted when no value has been persisted. + /// Skill installation result with the honoured contract, or a typed request/negotiation refusal. /// ///
/// @@ -4178,32 +6754,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn get_persisted_remote_steerable( + pub async fn apply_uninstall( &self, - params: SessionsGetPersistedRemoteSteerableRequest, - ) -> Result { + params: SkillApplyUninstallRequest, + ) -> 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_APPLYUNINSTALL, 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. + /// Discovers skills across global and project sources. /// - /// Wire method: `sessions.close`. + /// Wire method: `skills.discover`. /// /// # Parameters /// - /// * `params` - Session ID to close. + /// * `params` - Optional project paths and additional skill directories to include in discovery. /// /// # 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. + /// Skills discovered across global and project sources. /// ///
/// @@ -4212,26 +6785,26 @@ 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 discover(&self, params: SkillsDiscoverRequest) -> 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_DISCOVER, 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. /// ///
/// @@ -4240,25 +6813,29 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn bulk_delete( + pub async fn get_discovery_paths( &self, - params: SessionsBulkDeleteRequest, - ) -> Result { + params: SkillsGetDiscoveryPathsRequest, + ) -> 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. + /// 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.delete`. + /// Wire method: `skills.getDiscoveryPaths`. /// /// # Parameters /// - /// * `params` - Session ID to delete from disk. + /// * `params` - Optional project paths to enumerate. + /// + /// # Returns + /// + /// Canonical locations where skills can be created so the runtime will recognize them. /// ///
/// @@ -4267,26 +6844,35 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub(crate) async fn delete(&self, params: SessionsDeleteRequest) -> Result<(), Error> { + /// + /// Accepts [`SkillsGetDiscoveryPathsOptions`], including inputs added after [`SkillsGetDiscoveryPathsRequest`]. + pub async fn get_discovery_paths_with_options( + &self, + params: SkillsGetDiscoveryPathsOptions, + ) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_DELETE, Some(wire_params)) + .call(rpc_methods::SKILLS_GETDISCOVERYPATHS, Some(wire_params)) .await?; - Ok(()) + Ok(serde_json::from_value(_value)?) } +} - /// Deletes sessions older than the given threshold, with optional dry-run and exclusion list. +/// `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.pruneOld`. + /// Wire method: `skills.config.setDisabledSkills`. /// /// # 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` - Skill names to mark as disabled in global configuration, replacing any previous list. /// ///
/// @@ -4295,29 +6881,28 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn prune_old( + pub async fn set_disabled_skills( &self, - params: SessionsPruneOldRequest, - ) -> Result { + params: SkillsConfigSetDisabledSkillsRequest, + ) -> 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_SETDISABLEDSKILLS, + Some(wire_params), + ) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } - /// Flushes a session's pending events to disk. + /// Atomically adds or removes one skill from the disabled list. /// - /// Wire method: `sessions.save`. + /// Wire method: `skills.config.setSkillDisabled`. /// /// # 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). + /// * `params` - Adds or removes a single skill from the global disabled list, leaving every other entry untouched. /// ///
/// @@ -4326,26 +6911,40 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn save(&self, params: SessionsSaveRequest) -> Result { + pub async fn set_skill_disabled( + &self, + params: SkillsConfigSetSkillDisabledRequest, + ) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self .client - .call(rpc_methods::SESSIONS_SAVE, Some(wire_params)) + .call( + rpc_methods::SKILLS_CONFIG_SETSKILLDISABLED, + Some(wire_params), + ) .await?; - Ok(serde_json::from_value(_value)?) + Ok(()) } +} - /// Releases the in-use lock held by this process for a session. +/// `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. /// - /// 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. /// ///
/// @@ -4354,29 +6953,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. /// ///
/// @@ -4385,29 +6984,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. /// ///
/// @@ -4416,29 +7015,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. /// ///
/// @@ -4447,29 +7046,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. /// ///
/// @@ -4478,32 +7077,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. /// ///
/// @@ -4512,29 +7119,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. /// ///
/// @@ -4543,32 +7170,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. /// ///
/// @@ -4577,32 +7204,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. /// ///
/// @@ -4611,29 +7238,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. /// ///
/// @@ -4642,29 +7269,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`. + /// 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. /// - /// # Parameters - /// - /// * `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. /// ///
/// @@ -4673,32 +7299,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. - /// - /// Wire method: `sessions.setRemoteControlSteering`. - /// - /// # Parameters + /// 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. /// - /// * `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. /// ///
/// @@ -4707,28 +7323,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. /// ///
/// @@ -4737,26 +7356,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. /// ///
/// @@ -4765,25 +7390,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. /// ///
/// @@ -4792,81 +7421,66 @@ 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. /// ///
/// /// **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, - } + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub(crate) async fn reset_staff_model_selection_once( + &self, + params: UserSettingsResetStaffModelSelectionOnceRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::USER_SETTINGS_RESETSTAFFMODELSELECTIONONCE, + Some(wire_params), + ) + .await?; + 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. /// ///
/// @@ -4875,29 +7489,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. /// ///
/// @@ -4906,29 +7523,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. /// ///
/// @@ -4937,29 +7554,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. /// ///
/// @@ -4968,29 +7588,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. /// ///
/// @@ -4999,26 +7618,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. /// ///
/// @@ -5027,33 +7646,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. /// ///
/// @@ -5062,28 +7673,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. /// ///
/// @@ -5092,40 +7697,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. /// ///
/// @@ -5134,29 +7730,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. /// ///
/// @@ -5165,29 +7761,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. /// ///
/// @@ -5196,29 +7792,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. /// ///
/// @@ -5227,29 +7826,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. /// ///
/// @@ -5258,40 +7853,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. /// ///
/// @@ -5300,45 +7881,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. /// ///
/// @@ -5347,22 +7908,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. /// ///
/// @@ -5371,13 +7936,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)?) } } @@ -11008,6 +13576,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`. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 8d0e4d9b65..9770b046d2 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, @@ -2550,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 @@ -2568,10 +2577,6 @@ pub struct ResponsesReasoning { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionCompactionCompleteData { - /// Legacy active-workflow reminder retained for replay compatibility - #[doc(hidden)] - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) active_factory_summary: Option, /// Authoritative active-workflow reminder appended to the compacted context #[doc(hidden)] #[serde(skip_serializing_if = "Option::is_none")] @@ -2677,6 +2682,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 @@ -3110,6 +3121,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 @@ -5681,9 +5698,6 @@ pub struct SubagentStartedData { /// Whether the sub-agent runs synchronously or in the background. #[serde(skip_serializing_if = "Option::is_none")] pub execution_mode: Option, - /// Legacy root id of the workflow run that spawned this sub-agent. New consumers should use workflowRunId. - #[serde(skip_serializing_if = "Option::is_none")] - pub factory_run_id: Option, /// Model the sub-agent will run with, when known at start. #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, @@ -7029,6 +7043,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, @@ -8977,6 +8997,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)] diff --git a/rust/src/hooks.rs b/rust/src/hooks.rs index 3b2af37cb1..37a0fc0423 100644 --- a/rust/src/hooks.rs +++ b/rust/src/hooks.rs @@ -21,6 +21,38 @@ pub struct HookContext { pub session_id: SessionId, } +/// Runtime-provided actor metadata for a hook invocation. +/// +/// Available through [`SessionHooks::on_hook_with_actor`] without changing +/// legacy hook input structs or their construction patterns. +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HookActor { + #[serde(default)] + agent_id: Option, + #[serde(default)] + agent_type: Option, + #[serde(default)] + parent_agent_id: Option, +} + +impl HookActor { + /// Subagent actor ID, absent for root hooks and older runtimes. + pub fn agent_id(&self) -> Option<&str> { + self.agent_id.as_deref() + } + + /// Subagent type, when supplied by the runtime. + pub fn agent_type(&self) -> Option<&str> { + self.agent_type.as_deref() + } + + /// Immediate parent actor ID, when supplied by the runtime. + pub fn parent_agent_id(&self) -> Option<&str> { + self.parent_agent_id.as_deref() + } +} + /// Input for the `preToolUse` hook — received before a tool executes. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -271,8 +303,9 @@ pub struct SessionEndInput { /// The last assistant message. #[serde(default)] pub final_message: Option, - /// Error message, if the session ended due to an error. - #[serde(default)] + /// Error message, if the session ended due to an error. The CLI sends an + /// error object here (`{"message"}`); see `HookErrorWire`. + #[serde(default, deserialize_with = "optional_hook_error_text")] pub error: Option, } @@ -302,7 +335,9 @@ pub struct ErrorOccurredInput { /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, - /// The error message. + /// The error message. The CLI sends an error object here + /// (`{"name", "message", "stack"}`); see `HookErrorWire`. + #[serde(deserialize_with = "hook_error_text")] pub error: String, /// Context where the error occurred: `"model_call"`, `"tool_execution"`, `"system"`, `"user_input"`. pub error_context: String, @@ -310,6 +345,46 @@ pub struct ErrorOccurredInput { pub recoverable: bool, } +/// The two wire shapes of a hook input's `error` field. +/// +/// The CLI sends the `error` of the `errorOccurred` and `sessionEnd` inputs as +/// an error object (`{"name", "message", "stack"}`), while the public fields +/// stay strings. A string is the text, an object's string `message` is the +/// text, and an object without one keeps its compact JSON form. +#[derive(Deserialize)] +#[serde(untagged)] +enum HookErrorWire { + Text(String), + Object(serde_json::Map), +} + +impl HookErrorWire { + fn into_text(self) -> String { + match self { + Self::Text(text) => text, + Self::Object(mut object) => match object.get_mut("message") { + Some(Value::String(message)) => std::mem::take(message), + _ => Value::Object(object).to_string(), + }, + } + } +} + +fn hook_error_text<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + HookErrorWire::deserialize(deserializer).map(HookErrorWire::into_text) +} + +fn optional_hook_error_text<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + Option::::deserialize(deserializer) + .map(|wire| wire.map(HookErrorWire::into_text)) +} + /// Output for the `errorOccurred` hook. #[derive(Debug, Clone, Default, Serialize)] #[serde(rename_all = "camelCase")] @@ -612,6 +687,14 @@ impl HookOutput { /// sets automatically). #[async_trait] pub trait SessionHooks: Send + Sync + 'static { + /// Receives typed hook input and runtime-provided actor metadata. + /// + /// The default delegates to [`Self::on_hook`], preserving existing + /// whole-event and per-hook implementations. + async fn on_hook_with_actor(&self, event: HookEvent, _actor: HookActor) -> HookOutput { + self.on_hook(event).await + } + /// Top-level dispatch. The default implementation fans out to the /// per-hook methods below; override this only if you want a single /// matching point across all hook types. @@ -818,6 +901,7 @@ pub(crate) async fn dispatch_hook( let ctx = HookContext { session_id: session_id.clone(), }; + let actor_input = raw_input.clone(); let event = match hook_type { "preToolUse" => { @@ -879,7 +963,8 @@ pub(crate) async fn dispatch_hook( }; let dispatch_start = Instant::now(); - let output = hooks.on_hook(event).await; + let actor = serde_json::from_value(actor_input)?; + let output = hooks.on_hook_with_actor(event, actor).await; tracing::debug!( elapsed_ms = dispatch_start.elapsed().as_millis(), session_id = %session_id, diff --git a/rust/src/hooks/tests.rs b/rust/src/hooks/tests.rs index e8a8179c41..6294c70bcd 100644 --- a/rust/src/hooks/tests.rs +++ b/rust/src/hooks/tests.rs @@ -8,6 +8,234 @@ use super::*; struct TestHooks; +#[test] +fn legacy_inputs_preserve_exact_literals_and_destructuring() { + macro_rules! legacy_input { + ($input:ident { $($field:ident: $value:expr),* $(,)? }) => { + let input = $input { + session_id: "root".to_owned(), + timestamp: 1.0, + working_directory: PathBuf::from("work"), + $($field: $value,)* + }; + let $input { session_id, timestamp, working_directory, $($field,)* } = input; + assert_eq!(session_id, "root"); + assert_eq!(timestamp, 1.0); + assert_eq!(working_directory, PathBuf::from("work")); + let _ = ($($field,)*); + }; + } + legacy_input!(PreToolUseInput { + tool_name: "probe".to_owned(), + tool_args: serde_json::json!({}) + }); + legacy_input!(PreMcpToolCallInput { + server_name: "probe".to_owned(), + tool_name: "probe".to_owned(), + arguments: serde_json::json!({}), + tool_call_id: None, + meta: None + }); + legacy_input!(PostToolUseInput { + tool_name: "probe".to_owned(), + tool_args: serde_json::json!({}), + tool_result: serde_json::json!({}) + }); + legacy_input!(PostToolUseFailureInput { + tool_name: "probe".to_owned(), + tool_args: serde_json::json!({}), + error: "probe".to_owned() + }); + legacy_input!(UserPromptSubmittedInput { + prompt: "probe".to_owned() + }); + legacy_input!(UserPromptTransformedInput { + prompt: "probe".to_owned(), + transformed_prompt: "probe".to_owned() + }); + legacy_input!(SessionStartInput { + source: "new".to_owned(), + initial_prompt: None + }); + legacy_input!(SessionEndInput { + reason: "complete".to_owned(), + final_message: None, + error: None + }); + legacy_input!(ErrorOccurredInput { + error: "probe".to_owned(), + error_context: "model_call".to_owned(), + recoverable: false + }); + legacy_input!(AgentStopInput { + stop_reason: None, + transcript_path: None, + stop_hook_active: None + }); + legacy_input!(SubagentStartInput { + transcript_path: PathBuf::from("parent.jsonl"), + agent_name: "explore".to_owned(), + agent_display_name: None, + agent_description: None + }); + legacy_input!(SubagentStopInput { + transcript_path: PathBuf::from("parent.jsonl"), + agent_name: "explore".to_owned(), + agent_type: "explore".to_owned(), + agent_id: None, + agent_display_name: None, + agent_description: None, + stop_reason: "end_turn".to_owned(), + response: "complete".to_owned() + }); +} + +#[tokio::test] +async fn typed_dispatch_retains_optional_child_identity_and_old_root_inputs() { + struct Capture(std::sync::Mutex>); + #[async_trait] + impl SessionHooks for Capture { + async fn on_hook_with_actor(&self, event: HookEvent, actor: HookActor) -> HookOutput { + *self.0.lock().unwrap() = Some((format!("{event:?}"), actor)); + HookOutput::None + } + } + let hooks = Capture(std::sync::Mutex::new(None)); + for hook in HOOK_IDENTITY_TYPES { + for enriched in [true, false] { + dispatch_hook( + &hooks, + &SessionId::new("callback-owner"), + hook, + hook_identity_input(hook, enriched), + ) + .await + .unwrap(); + let (actual, actor) = hooks.0.lock().unwrap().take().unwrap(); + assert!( + actual.contains("reported-session") && actual.contains("callback-owner"), + "{hook}: {actual}" + ); + assert_eq!( + actor.agent_id(), + enriched.then_some("child-actor"), + "{hook}" + ); + assert_eq!( + actor.agent_type(), + (enriched || hook == "subagentStop").then_some("explore"), + "{hook}" + ); + assert_eq!( + actor.parent_agent_id(), + enriched.then_some("parent-actor"), + "{hook}" + ); + } + } +} + +const HOOK_IDENTITY_TYPES: [&str; 12] = [ + "preToolUse", + "preMcpToolCall", + "postToolUse", + "postToolUseFailure", + "userPromptSubmitted", + "userPromptTransformed", + "sessionStart", + "sessionEnd", + "errorOccurred", + "agentStop", + "subagentStart", + "subagentStop", +]; + +fn hook_identity_input(hook: &str, enriched: bool) -> Value { + let mut input = serde_json::json!({ + "sessionId": "reported-session", "timestamp": 1, "cwd": "work", + "toolName": "probe", "toolArgs": {}, "toolResult": {}, "error": "probe", + "serverName": "probe", "arguments": {}, "prompt": "probe", + "transformedPrompt": "probe", "source": "new", "reason": "complete", + "errorContext": "tool_execution", "recoverable": false, + "transcriptPath": "parent.jsonl", "agentName": "explore", + "agentType": "explore", "stopReason": "end_turn", "response": "complete" + }); + if enriched { + input["agentId"] = "child-actor".into(); + input["parentAgentId"] = "parent-actor".into(); + } else if hook != "subagentStop" { + input.as_object_mut().unwrap().remove("agentType"); + } + input +} + +#[tokio::test] +async fn default_actor_dispatch_preserves_legacy_handlers_for_all_hook_inputs() { + struct Legacy(std::sync::Mutex>); + #[async_trait] + impl SessionHooks for Legacy { + async fn on_hook(&self, event: HookEvent) -> HookOutput { + self.0.lock().unwrap().push(format!("{event:?}")); + HookOutput::None + } + } + let legacy = Legacy(std::sync::Mutex::new(Vec::new())); + for hook in HOOK_IDENTITY_TYPES { + for enriched in [true, false] { + let result = dispatch_hook( + &legacy, + &SessionId::new("callback-owner"), + hook, + hook_identity_input(hook, enriched), + ) + .await + .unwrap(); + assert_eq!(result, serde_json::json!({"output": {}}), "{hook}"); + } + } + let events = legacy.0.lock().unwrap(); + assert_eq!(events.len(), 24); + for event in events.iter() { + assert!( + event.contains("reported-session") && event.contains("callback-owner"), + "{event}" + ); + } +} + +#[tokio::test] +async fn actor_metadata_does_not_use_tool_arguments_or_null_fields() { + struct Actorless; + #[async_trait] + impl SessionHooks for Actorless { + async fn on_hook_with_actor(&self, _event: HookEvent, actor: HookActor) -> HookOutput { + assert_eq!(actor.agent_id(), None); + assert_eq!(actor.agent_type(), None); + assert_eq!(actor.parent_agent_id(), None); + HookOutput::None + } + } + for null_fields in [false, true] { + let mut input = hook_identity_input("preToolUse", false); + input["toolArgs"] = serde_json::json!({ + "agentId": "untrusted", "agentType": "untrusted", "parentAgentId": "untrusted" + }); + if null_fields { + for key in ["agentId", "agentType", "parentAgentId"] { + input[key] = Value::Null; + } + } + dispatch_hook( + &Actorless, + &SessionId::new("callback-owner"), + "preToolUse", + input, + ) + .await + .unwrap(); + } +} + #[async_trait] impl SessionHooks for TestHooks { async fn on_hook(&self, event: HookEvent) -> HookOutput { @@ -483,6 +711,73 @@ async fn dispatch_error_occurred() { assert_eq!(result["output"]["retryCount"], 3); } +/// The object-shaped `error` the CLI sends reaches the hook as its `message`. +#[tokio::test] +async fn dispatch_error_occurred_with_the_clis_error_object() { + struct Recorder(std::sync::Mutex>); + #[async_trait] + impl SessionHooks for Recorder { + async fn on_error_occurred( + &self, + input: ErrorOccurredInput, + _ctx: HookContext, + ) -> Option { + *self.0.lock().unwrap() = Some(input); + None + } + } + + let hooks = Recorder(std::sync::Mutex::new(None)); + let input = serde_json::json!({ + "sessionId": "sess-1", + "error": {"name": "Error", "message": "model timeout", "stack": "Error: model timeout"}, + "errorContext": "model_call", + "recoverable": true, + "timestamp": 1790464212617_u64, + "cwd": "/tmp" + }); + let result = dispatch_hook(&hooks, &SessionId::new("sess-1"), "errorOccurred", input) + .await + .unwrap(); + assert_eq!(result["output"], serde_json::json!({})); + let seen = hooks.0.lock().unwrap().take().expect("hook delivered"); + assert_eq!(seen.error, "model timeout"); + assert_eq!(seen.error_context, "model_call"); +} + +#[test] +fn session_end_error_decodes_from_object_string_null_and_absent() { + let base = serde_json::json!({ + "reason": "error", + "sessionId": "sess-1", + "timestamp": 1790464235720_u64, + "cwd": "/tmp" + }); + let with = |error: Option| { + let mut value = base.clone(); + if let Some(error) = error { + value["error"] = error; + } + serde_json::from_value::(value) + .unwrap() + .error + }; + assert_eq!( + with(Some(serde_json::json!({"message": "retries exhausted"}))).as_deref(), + Some("retries exhausted") + ); + assert_eq!( + with(Some(Value::String("boom".to_string()))).as_deref(), + Some("boom") + ); + assert_eq!(with(Some(Value::Null)), None); + assert_eq!(with(None), None); + assert_eq!( + with(Some(serde_json::json!({"code": 7}))).as_deref(), + Some(r#"{"code":7}"#) + ); +} + #[tokio::test] async fn dispatch_agent_stop_block() { struct AgentStopHooks; diff --git a/rust/src/jsonrpc.rs b/rust/src/jsonrpc.rs index c602ca9f4d..246ff6bdf2 100644 --- a/rust/src/jsonrpc.rs +++ b/rust/src/jsonrpc.rs @@ -268,17 +268,60 @@ fn repair_lone_surrogates(body: &[u8]) -> Option> { /// One framed JSON-RPC message handed to the writer actor. /// -/// `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. When present, `ack` resolves once the bytes have been +/// `frame` is the fully serialized frame (header + body, written as +/// [`EncodedFrame::as_bytes`]); the caller pays the serde cost synchronously +/// before enqueueing so the actor never sees a `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, + frame: EncodedFrame, ack: Option>>, } +/// Room reserved in front of a frame's body for its header: the header's name, +/// the 20 digits of the longest `usize`, and the blank line. +const FRAME_PREFIX: usize = CONTENT_LENGTH_HEADER.len() + 20 + 4; + +/// One JSON-RPC frame, header and body, in a single buffer: the header is +/// written into room reserved in front of the body and ends right where the +/// body starts. +pub(crate) struct EncodedFrame { + buf: Vec, + start: usize, +} + +impl EncodedFrame { + /// The frame as it goes on the wire: the header, then the body. + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.buf[self.start..] + } +} + +/// Encodes one JSON-RPC frame, header and body, into a single buffer. +/// +/// The body is serialized once, behind room reserved for the header, and the +/// header is then written into that room. A large message, such as a +/// `sessionFs.readFile` reply carrying a long session's `events.jsonl`, is held +/// once, escaped, instead of as a serialized body plus a framed copy of it. The +/// buffer grows by doubling, as `serde_json::to_vec`'s does, so it can be up to +/// about twice the frame's size. +pub(crate) fn encode_frame(message: &T) -> Result { + // `serde_json::to_vec` starts at 128 bytes; the body gets the same room here. + let mut buf = Vec::with_capacity(FRAME_PREFIX + 128); + buf.extend_from_slice(&[0; FRAME_PREFIX]); + serde_json::to_writer(&mut buf, message)?; + let digits = (buf.len() - FRAME_PREFIX).to_string(); + let start = FRAME_PREFIX - CONTENT_LENGTH_HEADER.len() - digits.len() - 4; + let (name, rest) = buf[start..FRAME_PREFIX].split_at_mut(CONTENT_LENGTH_HEADER.len()); + let (length, blank_line) = rest.split_at_mut(digits.len()); + name.copy_from_slice(CONTENT_LENGTH_HEADER.as_bytes()); + length.copy_from_slice(digits.as_bytes()); + blank_line.copy_from_slice(b"\r\n\r\n"); + Ok(EncodedFrame { buf, start }) +} + /// Inbound requests that honor `$/cancelRequest`. /// /// The read loop registers each one synchronously before forwarding it. This @@ -525,7 +568,7 @@ impl JsonRpcClient { ) { while let Some(WriteCommand { frame, ack }) = rx.recv().await { let result = async { - writer.write_all(&frame).await?; + writer.write_all(frame.as_bytes()).await?; writer.flush().await?; Ok::<_, std::io::Error>(()) } @@ -932,9 +975,24 @@ impl JsonRpcClient { async fn write_message( write_tx: &mpsc::UnboundedSender, message: &T, + ) -> Result<(), Error> { + let frame = encode_frame(message)?; + Self::send_frame(write_tx, frame).await + } + + /// Send a frame already encoded with [`encode_frame`] and wait for the + /// writer actor's acknowledgement, so a caller can drop what it encoded + /// from before the frame is written. + pub(crate) async fn write_frame(&self, frame: EncodedFrame) -> Result<(), Error> { + Self::send_frame(&self.write_tx, frame).await + } + + async fn send_frame( + write_tx: &mpsc::UnboundedSender, + frame: EncodedFrame, ) -> Result<(), Error> { let (ack_tx, ack_rx) = oneshot::channel(); - Self::enqueue_message(write_tx, message, Some(ack_tx))?; + Self::enqueue_frame(write_tx, frame, Some(ack_tx))?; match ack_rx.await { Ok(Ok(())) => Ok(()), Ok(Err(e)) => Err(Error::from(e)), @@ -950,13 +1008,17 @@ impl JsonRpcClient { 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); + let frame = encode_frame(message)?; + Self::enqueue_frame(write_tx, frame, ack) + } + /// Hand an encoded frame to the writer actor. When present, `ack` resolves + /// once the frame is flushed. + fn enqueue_frame( + write_tx: &mpsc::UnboundedSender, + frame: EncodedFrame, + ack: Option>>, + ) -> Result<(), Error> { write_tx.send(WriteCommand { frame, ack }).map_err(|_| { Error::from(std::io::Error::new( std::io::ErrorKind::BrokenPipe, diff --git a/rust/src/jsonrpc/tests.rs b/rust/src/jsonrpc/tests.rs index 0b7f154c83..4242151a49 100644 --- a/rust/src/jsonrpc/tests.rs +++ b/rust/src/jsonrpc/tests.rs @@ -138,6 +138,53 @@ fn request_serializes_camel_case() { assert!(json.contains(r#""method":"ping""#)); } +/// The frame the published framing wrote: the header, then `to_vec`'s body. +fn published_frame(message: &T) -> Vec { + let body = serde_json::to_vec(message).unwrap(); + let mut frame = format!("Content-Length: {}\r\n\r\n", body.len()).into_bytes(); + frame.extend_from_slice(&body); + frame +} + +#[test] +fn encode_frame_writes_the_published_frame() { + let message = serde_json::json!({ + "jsonrpc": "2.0", + "id": 7, + "result": {"content": "a \"quoted\" line\n"} + }); + assert_eq!( + encode_frame(&message).unwrap().as_bytes(), + published_frame(&message) + ); + // The length gains a digit at 10, 100, 1000 and 10000 bytes. Each body is a + // JSON string: its characters and two quotes. + for body_len in [9, 10, 99, 100, 999, 1000, 9999, 10000] { + let message = "x".repeat(body_len - 2); + let frame = encode_frame(&message).unwrap(); + assert!( + frame + .as_bytes() + .starts_with(format!("Content-Length: {body_len}\r\n\r\n").as_bytes()) + ); + assert_eq!(frame.as_bytes(), published_frame(&message)); + } +} + +#[test] +fn encode_frame_writes_the_header_into_the_room_before_the_body() { + // A 100-byte body: a JSON string of 98 characters. + let message = "x".repeat(98); + let frame = encode_frame(&message).unwrap(); + let header: &[u8] = b"Content-Length: 100\r\n\r\n"; + assert_eq!(frame.start, FRAME_PREFIX - header.len()); + assert_eq!(&frame.buf[frame.start..FRAME_PREFIX], header); + assert_eq!( + &frame.buf[FRAME_PREFIX..], + serde_json::to_vec(&message).unwrap().as_slice() + ); +} + #[test] fn notification_without_params_omits_field() { let n = JsonRpcNotification { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 090bbaf85f..6db5bf41b0 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2586,6 +2586,11 @@ impl Client { self.inner.rpc.write(response).await } + /// Send a frame already encoded with `jsonrpc::encode_frame`. + pub(crate) async fn send_frame(&self, frame: jsonrpc::EncodedFrame) -> Result<()> { + self.inner.rpc.write_frame(frame).await + } + /// Reconstruct a [`Client`] handle from a shared inner pointer. pub(crate) fn from_inner(inner: Arc) -> Self { Self { 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/session.rs b/rust/src/session.rs index 1ba8325095..3e1620fb70 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -3369,9 +3369,12 @@ async fn handle_notification( }; let request = ElicitationRequest { message: elicitation_data.message, + // Forward the wire schema verbatim: the generated type keys + // `properties` by `HashMap`, so re-serializing it scrambles + // the form's field order and drops keys it does not model. requested_schema: elicitation_data .requested_schema - .map(|s| serde_json::to_value(s).unwrap_or(Value::Null)), + .and(notification.event.data.get("requestedSchema").cloned()), mode: elicitation_data.mode.map(|m| match m { crate::generated::session_events::ElicitationRequestedMode::Form => { crate::types::ElicitationMode::Form diff --git a/rust/src/session_fs_dispatch.rs b/rust/src/session_fs_dispatch.rs index 06cbb016b9..577954f03d 100644 --- a/rust/src/session_fs_dispatch.rs +++ b/rust/src/session_fs_dispatch.rs @@ -30,24 +30,49 @@ use crate::{Client, JsonRpcRequest, JsonRpcResponse, error_codes}; const MAX_BINARY_BYTES: usize = (64 * 1024 * 1024 - 1024) / 4 * 3; +/// The success response [`respond`] encodes: [`JsonRpcResponse`]'s fields and +/// their names, with the typed result in place of a `serde_json::Value`. +#[derive(Serialize)] +struct TypedResponse<'a, T: Serialize> { + jsonrpc: &'static str, + id: u64, + result: &'a T, +} + /// Helper: serialize a typed result, send the response. +/// +/// The result is encoded straight into its frame, with no `serde_json::Value` +/// copy of it, and dropped before the frame is written. A large reply, such as +/// a `readFile` of a long session's `events.jsonl` on resume, is then held +/// once, escaped, while the frame is written. async fn respond(client: &Client, request_id: u64, result: T) { - let value = match serde_json::to_value(&result) { - Ok(v) => v, + let frame = match crate::jsonrpc::encode_frame(&TypedResponse { + jsonrpc: "2.0", + id: request_id, + result: &result, + }) { + Ok(frame) => frame, Err(e) => { warn!(error = %e, "failed to serialize sessionFs response"); send_error(client, request_id, "serialization failure").await; return; } }; - let _ = client - .send_response(&JsonRpcResponse { - jsonrpc: "2.0".to_string(), - id: request_id, - result: Some(value), - error: None, - }) - .await; + drop(result); + let _ = client.send_frame(frame).await; +} + +/// [`respond`] for a result holding `HashMap` rows (the sqlite replies). It +/// goes through a `serde_json::Value` first, so each row's keys keep the +/// sorted order a `Value` writes; typed, they would go out in hash order. +async fn respond_sorted(client: &Client, request_id: u64, result: T) { + match serde_json::to_value(&result) { + Ok(value) => respond(client, request_id, value).await, + Err(e) => { + warn!(error = %e, "failed to serialize sessionFs response"); + send_error(client, request_id, "serialization failure").await; + } + } } async fn send_error(client: &Client, request_id: u64, message: &str) { @@ -470,7 +495,7 @@ pub(crate) async fn sqlite_query( rows_affected: 0, }, }; - respond(client, id, result).await; + respond_sorted(client, id, result).await; } pub(crate) async fn sqlite_transaction( @@ -533,7 +558,7 @@ pub(crate) async fn sqlite_transaction( }), }, }; - respond(client, id, result).await; + respond_sorted(client, id, result).await; } pub(crate) async fn sqlite_exists( 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/tool.rs b/rust/src/tool.rs index 12274dbb8b..bf0709337e 100644 --- a/rust/src/tool.rs +++ b/rust/src/tool.rs @@ -59,6 +59,9 @@ pub fn schema_for() -> serde_json::Value { /// [`Tool::parameters`](crate::types::Tool::parameters) map shape /// expected by the protocol. /// +/// Object keys are sorted at every depth, so the serialized tool definition +/// does not depend on how the schema was built. +/// /// Panics if the input is not a JSON object — tool parameter schemas /// are always top-level objects (`{"type": "object", ...}`). Pair with /// `schema_for` (available with the `derive` feature) or a @@ -87,7 +90,31 @@ pub fn tool_parameters(schema: serde_json::Value) -> IndexMap Result, serde_json::Error> { - serde_json::from_value(schema) + serde_json::from_value(sort_object_keys(schema)) +} + +/// Sort object keys at every depth so a schema serializes identically however +/// it was built. With `serde_json/preserve_order` enabled anywhere in the +/// dependency graph, a schema converted from a `HashMap` would otherwise keep +/// that map's per-process iteration order and change the tool definitions the +/// model sees between sessions, defeating prompt caching. +fn sort_object_keys(value: serde_json::Value) -> serde_json::Value { + match value { + serde_json::Value::Object(map) => { + let mut entries: Vec<_> = map.into_iter().collect(); + entries.sort_by(|(a, _), (b, _)| a.cmp(b)); + serde_json::Value::Object( + entries + .into_iter() + .map(|(key, value)| (key, sort_object_keys(value))) + .collect(), + ) + } + serde_json::Value::Array(items) => { + serde_json::Value::Array(items.into_iter().map(sort_object_keys).collect()) + } + other => other, + } } /// Convert an MCP `CallToolResult` JSON value into a Copilot tool result. diff --git a/rust/src/tool/tests.rs b/rust/src/tool/tests.rs index d307202ce5..f753f8976c 100644 --- a/rust/src/tool/tests.rs +++ b/rust/src/tool/tests.rs @@ -73,6 +73,8 @@ fn tool_parameters_serialize_in_deterministic_order() { // Pin the exact top-level key order so a regression to any // order-randomizing container is caught, not just internal drift. + // Keys are sorted even when `serde_json/preserve_order` is enabled, + // as it is in this crate. let tool = build(); let keys: Vec<&str> = tool.parameters.keys().map(String::as_str).collect(); assert_eq!( @@ -81,6 +83,54 @@ fn tool_parameters_serialize_in_deterministic_order() { ); } +#[test] +fn tool_parameters_sort_keys_of_schemas_built_from_hash_maps() { + // A schema converted from a `HashMap` carries that map's per-process + // iteration order into an insertion-ordered `Value`. The tool definition + // must not depend on it, at any depth. + let properties: std::collections::HashMap = [ + "zeta", "alpha", "mu", "beta", "omega", "gamma", "kappa", "delta", + ] + .into_iter() + .map(|name| { + let field: std::collections::HashMap<&str, &str> = + [("type", "string"), ("description", name)].into(); + (name.to_string(), serde_json::to_value(field).unwrap()) + }) + .collect(); + let mut schema = serde_json::Map::new(); + schema.insert("type".into(), "object".into()); + schema.insert( + "properties".into(), + serde_json::to_value(properties).unwrap(), + ); + + let parameters = tool_parameters(serde_json::Value::Object(schema)); + + assert_eq!( + parameters.keys().map(String::as_str).collect::>(), + ["properties", "type"] + ); + let properties = parameters["properties"].as_object().unwrap(); + assert_eq!( + properties.keys().map(String::as_str).collect::>(), + [ + "alpha", "beta", "delta", "gamma", "kappa", "mu", "omega", "zeta" + ] + ); + for field in properties.values() { + assert_eq!( + field + .as_object() + .unwrap() + .keys() + .map(String::as_str) + .collect::>(), + ["description", "type"] + ); + } +} + #[test] fn convert_mcp_call_tool_result_collects_text_and_binary_content() { let result = convert_mcp_call_tool_result(&serde_json::json!({ diff --git a/rust/src/types.rs b/rust/src/types.rs index 9917693110..4a9d580329 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. /// @@ -2053,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 @@ -2158,12 +2174,18 @@ 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, /// When true, enables the session store for this session. pub enable_session_store: Option, + /// When true, requests the read-only GitHub cache store for this session. + /// + /// The runtime registers `github_sql` only when its `GITHUB_CACHE_STORE` + /// feature flag is enabled. Defaults to disabled. + pub enable_github_cache_store: Option, /// When true, enables skills for this session. pub enable_skills: Option, /// **Experimental.** Supplies session-scoped skills from the SDK host. @@ -2205,10 +2227,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>, @@ -2352,6 +2394,12 @@ pub struct SessionConfig { /// defaults remain mutable. Set via /// [`with_enforce_managed_model_defaults`](Self::with_enforce_managed_model_defaults). pub enforce_managed_model_defaults: Option, + /// Declares that a human drives this session's conversation, enabling + /// user-message analysis telemetry for user-authored messages. Automated + /// messages remain excluded. Defaults to false for SDK sessions. Ignored for + /// cloud sessions. Set via + /// [`with_interactive_user`](Self::with_interactive_user). + pub interactive_user: Option, /// Optional managed-settings layer injected at session bootstrap. Currently /// carries a [`permissions`](ManagedSettingsPermissions) object that composes /// restrictively with any server- or device-level managed settings. This @@ -2443,6 +2491,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) @@ -2498,6 +2547,7 @@ impl std::fmt::Debug for SessionConfig { &self.enable_host_git_operations, ) .field("enable_session_store", &self.enable_session_store) + .field("enable_github_cache_store", &self.enable_github_cache_store) .field("enable_skills", &self.enable_skills) .field( "skill_provider", @@ -2505,6 +2555,8 @@ 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) @@ -2553,6 +2605,7 @@ impl std::fmt::Debug for SessionConfig { "enforce_managed_model_defaults", &self.enforce_managed_model_defaults, ) + .field("interactive_user", &self.interactive_user) .field("enable_experimental_mode", &self.enable_experimental_mode) .field("managed_settings", &self.managed_settings) .field( @@ -2605,6 +2658,7 @@ impl Default for SessionConfig { fn default() -> Self { Self { session_id: None, + name: None, model: None, allowed_models: None, client_name: None, @@ -2638,12 +2692,15 @@ impl Default for SessionConfig { enable_file_hooks: None, enable_host_git_operations: None, enable_session_store: None, + enable_github_cache_store: None, enable_skills: None, skill_provider: None, embedding_cache_storage: None, 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, @@ -2679,6 +2736,7 @@ impl Default for SessionConfig { exp_assignments: None, enable_managed_settings: None, enforce_managed_model_defaults: None, + interactive_user: None, managed_settings: None, session_fs_provider: None, permission_handler: None, @@ -2772,6 +2830,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, @@ -2806,6 +2865,7 @@ impl SessionConfig { enable_file_hooks: self.enable_file_hooks, enable_host_git_operations: self.enable_host_git_operations, enable_session_store: self.enable_session_store, + enable_github_cache_store: self.enable_github_cache_store, enable_skills: self.enable_skills, has_skill_provider: has_skill_provider.then_some(true), request_user_input, @@ -2817,6 +2877,8 @@ 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, @@ -2853,6 +2915,7 @@ impl SessionConfig { exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, enforce_managed_model_defaults: self.enforce_managed_model_defaults, + interactive_user: self.interactive_user, is_experimental_mode: self.enable_experimental_mode, managed_settings: self.managed_settings, }; @@ -2998,6 +3061,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()); @@ -3228,6 +3300,12 @@ impl SessionConfig { self } + /// Set [`Self::enable_github_cache_store`]. + pub fn with_enable_github_cache_store(mut self, value: bool) -> Self { + self.enable_github_cache_store = Some(value); + self + } + /// Set [`Self::enable_skills`]. pub fn with_enable_skills(mut self, value: bool) -> Self { self.enable_skills = Some(value); @@ -3268,6 +3346,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 @@ -3584,6 +3682,12 @@ impl SessionConfig { self } + /// Declare that a human drives this session's conversation. + pub fn with_interactive_user(mut self, interactive_user: bool) -> Self { + self.interactive_user = Some(interactive_user); + self + } + /// Inject a managed-settings layer (currently permission rules) at session /// bootstrap. This layer is startup-only and is not persisted, so it must be /// re-supplied on resume to remain in effect. Can be combined with @@ -3694,12 +3798,18 @@ 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, /// When true, enables the session store on resume. pub enable_session_store: Option, + /// When true, requests the read-only GitHub cache store on resume. + /// + /// The runtime registers `github_sql` only when its `GITHUB_CACHE_STORE` + /// feature flag is enabled. Defaults to disabled. + pub enable_github_cache_store: Option, /// When true, enables skills on resume. pub enable_skills: Option, /// **Experimental.** Supplies session-scoped skills from the SDK host. @@ -3718,10 +3828,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>, @@ -3828,6 +3958,11 @@ pub struct ResumeSessionConfig { /// Re-applies managed model lock enforcement on resume. See /// [`SessionConfig::enforce_managed_model_defaults`]. pub enforce_managed_model_defaults: Option, + /// Re-supplies that a human drives this session so a session loaded from + /// storage keeps the declaration. It is not persisted and is ignored if the + /// session is already active in the runtime. Set via + /// [`with_interactive_user`](Self::with_interactive_user). + pub interactive_user: Option, /// Optional managed-settings layer injected on resume. See /// [`SessionConfig::managed_settings`]. This layer is not persisted, so it /// must be re-supplied on resume to remain in effect; omitting it clears the @@ -3952,6 +4087,7 @@ impl std::fmt::Debug for ResumeSessionConfig { &self.enable_host_git_operations, ) .field("enable_session_store", &self.enable_session_store) + .field("enable_github_cache_store", &self.enable_github_cache_store) .field("enable_skills", &self.enable_skills) .field( "skill_provider", @@ -3959,6 +4095,8 @@ 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) @@ -4006,6 +4144,7 @@ impl std::fmt::Debug for ResumeSessionConfig { "enforce_managed_model_defaults", &self.enforce_managed_model_defaults, ) + .field("interactive_user", &self.interactive_user) .field("enable_experimental_mode", &self.enable_experimental_mode) .field("managed_settings", &self.managed_settings) .field( @@ -4124,6 +4263,7 @@ impl ResumeSessionConfig { enable_file_hooks: self.enable_file_hooks, enable_host_git_operations: self.enable_host_git_operations, enable_session_store: self.enable_session_store, + enable_github_cache_store: self.enable_github_cache_store, enable_skills: self.enable_skills, has_skill_provider: has_skill_provider.then_some(true), request_user_input, @@ -4135,6 +4275,8 @@ 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, @@ -4170,6 +4312,7 @@ impl ResumeSessionConfig { exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, enforce_managed_model_defaults: self.enforce_managed_model_defaults, + interactive_user: self.interactive_user, is_experimental_mode: self.enable_experimental_mode, managed_settings: self.managed_settings, suppress_resume_event: self.suppress_resume_event, @@ -4239,12 +4382,15 @@ impl ResumeSessionConfig { enable_file_hooks: None, enable_host_git_operations: None, enable_session_store: None, + enable_github_cache_store: None, enable_skills: None, skill_provider: None, embedding_cache_storage: None, 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, @@ -4279,6 +4425,7 @@ impl ResumeSessionConfig { exp_assignments: None, enable_managed_settings: None, enforce_managed_model_defaults: None, + interactive_user: None, managed_settings: None, session_fs_provider: None, suppress_resume_event: None, @@ -4637,6 +4784,12 @@ impl ResumeSessionConfig { self } + /// Set [`Self::enable_github_cache_store`]. + pub fn with_enable_github_cache_store(mut self, value: bool) -> Self { + self.enable_github_cache_store = Some(value); + self + } + /// Set [`Self::enable_skills`]. pub fn with_enable_skills(mut self, value: bool) -> Self { self.enable_skills = Some(value); @@ -4669,6 +4822,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 @@ -4991,6 +5164,12 @@ impl ResumeSessionConfig { self } + /// Re-supply that a human drives this session. + pub fn with_interactive_user(mut self, interactive_user: bool) -> Self { + self.interactive_user = Some(interactive_user); + self + } + /// Inject a managed-settings layer (currently permission rules) on resume. /// See [`SessionConfig::with_managed_settings`]. Must be re-supplied on /// resume; omitting it clears the previously injected layer. @@ -6575,6 +6754,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 0749d946bd..451fe35191 100644 --- a/rust/src/types/tests.rs +++ b/rust/src/types/tests.rs @@ -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() { diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 32c46bc85f..fe9aacbd17 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -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>, @@ -121,6 +123,11 @@ pub(crate) struct SessionCreateWire { pub enable_host_git_operations: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enable_session_store: Option, + #[serde( + rename = "enableGitHubCacheStore", + skip_serializing_if = "Option::is_none" + )] + pub enable_github_cache_store: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enable_skills: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -137,6 +144,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>, @@ -215,6 +226,8 @@ pub(crate) struct SessionCreateWire { #[serde(skip_serializing_if = "Option::is_none")] pub enforce_managed_model_defaults: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub interactive_user: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, #[serde(skip_serializing_if = "Option::is_none")] pub managed_settings: Option, @@ -295,6 +308,11 @@ pub(crate) struct SessionResumeWire { pub enable_host_git_operations: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enable_session_store: Option, + #[serde( + rename = "enableGitHubCacheStore", + skip_serializing_if = "Option::is_none" + )] + pub enable_github_cache_store: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enable_skills: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -311,6 +329,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>, @@ -394,6 +416,8 @@ pub(crate) struct SessionResumeWire { #[serde(skip_serializing_if = "Option::is_none")] pub enforce_managed_model_defaults: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub interactive_user: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, #[serde(skip_serializing_if = "Option::is_none")] pub managed_settings: Option, diff --git a/rust/tests/e2e/client_options.rs b/rust/tests/e2e/client_options.rs index c516f84be6..85770746a1 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") @@ -46,6 +51,7 @@ async fn should_forward_advanced_session_creation_options_to_the_cli() { .with_enable_file_hooks(false) .with_enable_host_git_operations(false) .with_enable_session_store(false) + .with_enable_github_cache_store(true) .with_enable_skills(false) .with_working_directory(working_dir.clone()) .with_streaming(true) @@ -95,6 +101,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")), @@ -112,6 +119,7 @@ async fn should_forward_advanced_session_creation_options_to_the_cli() { ("enableFileHooks", json!(false)), ("enableHostGitOperations", json!(false)), ("enableSessionStore", json!(false)), + ("enableGitHubCacheStore", json!(true)), ("enableSkills", json!(false)), ("workingDirectory", json!(path_string(&working_dir))), ("streaming", json!(true)), 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/rust/tests/session_fs_reply_test.rs b/rust/tests/session_fs_reply_test.rs new file mode 100644 index 0000000000..7dc0fc56c5 --- /dev/null +++ b/rust/tests/session_fs_reply_test.rs @@ -0,0 +1,424 @@ +//! A sessionFs reply on its way out: the SDK encodes the provider's result once, +//! into a single frame buffer, and drops the result before the frame is +//! written. The wire stays as it was: every case asserts the exact frame bytes, +//! a `Content-Length` that states the body's length and the body the published +//! path wrote, `{"jsonrpc":"2.0","id":N,"result":R}` with `R` the result as a +//! `serde_json::Value` writes it (keys sorted). And a large `readFile` reply is +//! allocated once: a counting allocator records every allocation at least as +//! large as the reply's content, from the request's write until the reply's +//! last byte is read. +//! +//! 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::sync::{Arc, Mutex}; +use std::time::Duration; + +use async_trait::async_trait; +use github_copilot_sdk::Client; +use github_copilot_sdk::session::Session; +use github_copilot_sdk::session_fs::{ + DirEntry, DirEntryKind, FileInfo, FsError, FsErrorKind, SessionFsProvider, + SessionFsSqliteProvider, SessionFsSqliteQueryResult, SessionFsSqliteQueryType, +}; +use github_copilot_sdk::types::SessionConfig; +use serde_json::{Value, json}; +use serial_test::{parallel, serial}; +use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream, duplex}; +use tokio::time::timeout; + +/// A backstop against a hang, never a measurement. +const DEADLINE: Duration = Duration::from_secs(30); +/// The content of the counting case's `readFile` reply. Odd, so no buffer that +/// grows by doubling lands on it. +const CONTENT_BYTES: usize = 8 * 1024 * 1024 + 1; +/// The room the SDK reserves in front of a frame's body for its header +/// (`jsonrpc::FRAME_PREFIX`, private to the crate). +const HEADER_ROOM: usize = 40; + +/// The system allocator, recording the size of every allocation (and +/// reallocation) of at least [`WATCH_FROM`] bytes while it is non-zero. +struct LargeAllocationRecorder; + +static WATCH_FROM: AtomicUsize = AtomicUsize::new(0); +static LARGE_COUNT: AtomicUsize = AtomicUsize::new(0); +static LARGE: [AtomicUsize; 16] = [const { AtomicUsize::new(0) }; 16]; + +fn note(size: usize) { + let from = WATCH_FROM.load(Ordering::SeqCst); + if from != 0 && size >= from { + let slot = LARGE_COUNT.fetch_add(1, Ordering::SeqCst); + if let Some(entry) = LARGE.get(slot) { + entry.store(size, 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 LargeAllocationRecorder { + 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: LargeAllocationRecorder = LargeAllocationRecorder; + +/// A provider whose files are handed out once each, so a read allocates +/// nothing; every other answer is fixed. +struct FixedProvider { + files: Mutex>, +} + +impl FixedProvider { + fn with_file(path: &str, content: String) -> Arc { + Arc::new(Self { + files: Mutex::new(HashMap::from([(path.to_string(), content)])), + }) + } +} + +#[async_trait] +impl SessionFsProvider for FixedProvider { + async fn read_file(&self, path: &str) -> Result { + self.files + .lock() + .expect("the files lock") + .remove(path) + .ok_or_else(|| FsError::from(FsErrorKind::NotFound(path.to_string()))) + } + + async fn write_file( + &self, + _path: &str, + _content: &str, + _mode: Option, + ) -> Result<(), FsError> { + Ok(()) + } + + async fn stat(&self, _path: &str) -> Result { + Ok(FileInfo::new( + true, + false, + 42, + "2025-01-01T00:00:00Z", + "2025-01-02T00:00:00Z", + )) + } + + async fn readdir_with_types(&self, _path: &str) -> Result, FsError> { + Ok(vec![ + DirEntry::new("README.md", DirEntryKind::File), + DirEntry::new("src", DirEntryKind::Directory), + ]) + } + + fn sqlite(&self) -> Option<&dyn SessionFsSqliteProvider> { + Some(self) + } +} + +#[async_trait] +impl SessionFsSqliteProvider for FixedProvider { + async fn sqlite_query( + &self, + _query_type: SessionFsSqliteQueryType, + _query: &str, + _params: Option<&HashMap>, + ) -> Result, FsError> { + // Keys inserted out of order: the reply must still write them sorted. + let row = HashMap::from([ + ("zeta".to_string(), json!(1)), + ("alpha".to_string(), json!("a")), + ("mid".to_string(), json!(null)), + ("beta".to_string(), json!([true, 2.5])), + ]); + Ok(Some(SessionFsSqliteQueryResult { + columns: vec![ + "zeta".to_string(), + "alpha".to_string(), + "mid".to_string(), + "beta".to_string(), + ], + rows: vec![row], + rows_affected: 0, + last_insert_rowid: None, + })) + } + + async fn sqlite_exists(&self) -> Result { + Ok(true) + } +} + +/// The CLI's side of one session: the frames the SDK writes, and a writer for +/// the test's requests. +struct Server { + read: DuplexStream, + write: DuplexStream, + session_id: String, +} + +impl Server { + async fn write_frame(&mut self, message: &Value) { + let body = serde_json::to_vec(message).expect("a JSON message"); + let header = format!("Content-Length: {}\r\n\r\n", body.len()); + self.write + .write_all(header.as_bytes()) + .await + .expect("the header to the SDK"); + self.write + .write_all(&body) + .await + .expect("the body to the SDK"); + self.write.flush().await.expect("a flush"); + } + + /// Reads one frame's header and returns the body length it states. + async fn read_header(&mut self) -> usize { + let mut header = String::new(); + while !header.ends_with("\r\n\r\n") { + let byte = self.read.read_u8().await.expect("a header byte"); + header.push(char::from(byte)); + } + header + .trim_end() + .strip_prefix("Content-Length: ") + .expect("a Content-Length header") + .parse() + .expect("a decimal length") + } + + async fn read_frame(&mut self) -> Vec { + let length = self.read_header().await; + let mut body = vec![0; length]; + self.read + .read_exact(&mut body) + .await + .expect("the frame body"); + body + } + + async fn request(&mut self, id: u64, method: &str, mut params: Value) { + params["sessionId"] = json!(self.session_id); + self.write_frame(&json!({"jsonrpc": "2.0", "id": id, "method": method, "params": params})) + .await; + } +} + +/// A session with `provider` installed, the test answering `session.create`. +async fn start(provider: Arc) -> (Session, Server) { + let (client_write, server_read) = duplex(64 * 1024); + let (server_write, client_read) = duplex(64 * 1024); + let client = Client::from_streams(client_read, client_write, std::env::temp_dir()) + .expect("a client over the streams"); + let mut server = Server { + read: server_read, + write: server_write, + session_id: String::new(), + }; + let create = tokio::spawn(async move { + client + .create_session(SessionConfig::default().with_session_fs_provider(provider)) + .await + .expect("the session starts") + }); + let request: Value = serde_json::from_slice(&server.read_frame().await).expect("a request"); + assert_eq!(request["method"], "session.create"); + server.session_id = request["params"]["sessionId"] + .as_str() + .expect("session.create carries the session id") + .to_string(); + let reply = json!({ + "jsonrpc": "2.0", + "id": request["id"], + "result": {"sessionId": server.session_id, "workspacePath": "/tmp/workspace"} + }); + server.write_frame(&reply).await; + let session = timeout(DEADLINE, create) + .await + .expect("session.create is answered in time") + .expect("the create task"); + (session, server) +} + +/// The body the published path wrote for a reply to `id` whose result is +/// `result`: the response's fields in order, the result as a `Value` writes it. +fn published_body(id: u64, result: &Value) -> String { + let result = serde_json::to_string(result).expect("a JSON result"); + format!(r#"{{"jsonrpc":"2.0","id":{id},"result":{result}}}"#) +} + +/// Sends one request and returns the reply's result, after asserting that the +/// reply's bytes are exactly the published path's. +async fn reply_to(server: &mut Server, id: u64, method: &str, params: Value) -> Value { + server.request(id, method, params).await; + let body = timeout(DEADLINE, server.read_frame()) + .await + .expect("the reply arrives in time"); + let reply: Value = serde_json::from_slice(&body).expect("a JSON reply"); + let result = reply["result"].clone(); + assert_eq!( + String::from_utf8(body).expect("a UTF-8 body"), + published_body(id, &result), + "the reply to {method}" + ); + result +} + +/// The reply to a `sessionFs.readFile` of `path`, checked as [`reply_to`] does. +async fn read_file(server: &mut Server, id: u64, path: &str) -> Value { + reply_to(server, id, "sessionFs.readFile", json!({ "path": path })).await +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_read_file_reply_is_written_as_published_for_every_escape_class() { + let content = "quote \" backslash \\ newline \n return \r tab \t backspace \u{8} \ + formfeed \u{c} controls \u{1}\u{1f} del \u{7f} utf-8 \u{e9}\u{4e2d}\u{1f600}" + .to_string(); + let provider = FixedProvider::with_file("/file.txt", content.clone()); + let (_session, mut server) = start(provider).await; + let result = read_file(&mut server, 7, "/file.txt").await; + assert_eq!(result, json!({"content": content})); +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_missing_file_reply_is_written_as_published() { + let (_session, mut server) = start(FixedProvider::with_file("/file.txt", String::new())).await; + let result = read_file(&mut server, 8, "/missing.txt").await; + assert_eq!(result["error"]["code"], "ENOENT"); +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn write_file_stat_and_readdir_replies_are_written_as_published() { + let (_session, mut server) = start(FixedProvider::with_file("/file.txt", String::new())).await; + let written = reply_to( + &mut server, + 9, + "sessionFs.writeFile", + json!({"path": "/out.txt", "content": "x"}), + ) + .await; + assert_eq!(written, Value::Null); + let path = json!({"path": "/file.txt"}); + let stat = reply_to(&mut server, 10, "sessionFs.stat", path).await; + assert_eq!(stat["size"], 42); + let listing = reply_to( + &mut server, + 11, + "sessionFs.readdirWithTypes", + json!({"path": "/"}), + ) + .await; + assert_eq!(listing["entries"][1]["name"], "src"); +} + +/// A sqlite row is a `HashMap`: its keys must still go out sorted, as the +/// published path's `Value` wrote them. +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_sqlite_reply_writes_its_row_keys_sorted() { + let (_session, mut server) = start(FixedProvider::with_file("/file.txt", String::new())).await; + let result = reply_to( + &mut server, + 12, + "sessionFs.sqliteQuery", + json!({"queryType": "query", "query": "select 1"}), + ) + .await; + assert_eq!( + result["rows"][0], + json!({"alpha": "a", "beta": [true, 2.5], "mid": null, "zeta": 1}) + ); +} + +#[tokio::test(flavor = "multi_thread")] +#[parallel] +async fn a_six_mebibyte_reply_is_one_frame_written_as_published() { + let content = "0123456789abcdef".repeat(6 * 1024 * 1024 / 16); + let (_session, mut server) = start(FixedProvider::with_file("/big.txt", content.clone())).await; + let result = read_file(&mut server, 13, "/big.txt").await; + let length = result["content"].as_str().map(str::len); + assert_eq!(length, Some(content.len())); +} + +/// The mechanism: between the request's write and the reply's last byte, the +/// only allocation at least as large as the content is the reply's frame +/// buffer. It holds the frame behind the room reserved for the header and grows +/// by doubling, so it is at least the frame's length and less than twice the +/// frame plus that room. The published path allocated the content again (a +/// `Value` copy), a body grown by doubling, and a framed copy of the body. +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn a_read_file_reply_is_allocated_once_in_its_frame_buffer() { + // A quote every 64 bytes, so escaping changes the length. + let content: String = (0..CONTENT_BYTES) + .map(|i| if i % 64 == 63 { '"' } else { 'a' }) + .collect(); + let (_session, mut server) = start(FixedProvider::with_file("/events.jsonl", content)).await; + // Room for the escaped reply, allocated before the count starts. + let mut body = vec![0u8; 2 * CONTENT_BYTES + 4096]; + + LARGE_COUNT.store(0, Ordering::SeqCst); + WATCH_FROM.store(CONTENT_BYTES, Ordering::SeqCst); + server + .request(20, "sessionFs.readFile", json!({"path": "/events.jsonl"})) + .await; + let length = timeout(DEADLINE, server.read_header()) + .await + .expect("the reply arrives in time"); + timeout(DEADLINE, server.read.read_exact(&mut body[..length])) + .await + .expect("the reply's body arrives in time") + .expect("the reply's body"); + WATCH_FROM.store(0, Ordering::SeqCst); + + let count = LARGE_COUNT.load(Ordering::SeqCst); + let sizes: Vec = LARGE + .iter() + .take(count) + .map(|size| size.load(Ordering::SeqCst)) + .collect(); + let frame = format!("Content-Length: {length}\r\n\r\n").len() + length; + let most = 2 * (frame + HEADER_ROOM); + assert!( + sizes.len() == 1 && (frame..most).contains(&sizes[0]), + "allocations of at least the content's {CONTENT_BYTES} bytes while the reply was built \ + and written: {sizes:?}; want only the frame's buffer, of {frame} to {most} bytes" + ); + let reply: Value = serde_json::from_slice(&body[..length]).expect("a JSON reply"); + assert_eq!(reply["id"], 20); + assert_eq!( + reply["result"]["content"].as_str().map(str::len), + Some(CONTENT_BYTES) + ); +} diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 40c455549c..48a31e9e02 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -1646,6 +1646,7 @@ async fn create_session_omits_unset_diagnostics() { let request = read_framed(&mut server_read).await; assert_eq!(request["method"], "session.create"); assert!(request["params"].get("diagnostics").is_none()); + assert!(request["params"].get("interactiveUser").is_none()); let session_id = requested_session_id(&request).to_string(); server_respond_create(&mut server_write, &request, &session_id).await; @@ -1673,6 +1674,7 @@ async fn cold_resume_session_omits_unset_diagnostics() { let request = read_framed(&mut server_read).await; assert_eq!(request["method"], "session.resume"); assert!(request["params"].get("diagnostics").is_none()); + assert!(request["params"].get("interactiveUser").is_none()); server_respond_create(&mut server_write, &request, "diagnostics-default").await; respond_to_reload(&mut server_read, &mut server_write).await; @@ -1831,6 +1833,7 @@ async fn create_and_resume_send_managed_settings_permissions() { .create_session( SessionConfig::default() .with_enable_managed_settings(true) + .with_interactive_user(true) .with_managed_settings(managed), ) .await @@ -1841,6 +1844,7 @@ async fn create_and_resume_send_managed_settings_permissions() { let request = read_framed(&mut server_read).await; assert_eq!(request["method"], "session.create"); assert_eq!(request["params"]["enableManagedSettings"], true); + assert_eq!(request["params"]["interactiveUser"], true); let perms = &request["params"]["managedSettings"]["permissions"]; assert_eq!(perms["disableBypassPermissionsMode"], "allow-auto-only"); assert_eq!(perms["disableAssistedPermissionsMode"], true); @@ -1866,6 +1870,7 @@ async fn create_and_resume_send_managed_settings_permissions() { client .resume_session( ResumeSessionConfig::new(SessionId::from(session_id)) + .with_interactive_user(true) .with_managed_settings(managed), ) .await @@ -1875,6 +1880,7 @@ async fn create_and_resume_send_managed_settings_permissions() { let request = read_framed(&mut server_read).await; assert_eq!(request["method"], "session.resume"); + assert_eq!(request["params"]["interactiveUser"], true); assert_eq!( request["params"]["managedSettings"]["permissions"]["deny"][0], "shell(rm*)" @@ -1897,6 +1903,51 @@ async fn create_and_resume_send_managed_settings_permissions() { timeout(TIMEOUT, resume_handle).await.unwrap().unwrap(); } +#[tokio::test] +async fn create_and_resume_send_explicit_interactive_user_false() { + use github_copilot_sdk::types::ResumeSessionConfig; + + let (client, mut server_read, mut server_write) = make_client(); + + let create_handle = tokio::spawn({ + let client = client.clone(); + async move { + client + .create_session(SessionConfig::default().with_interactive_user(false)) + .await + .unwrap() + } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "session.create"); + assert_eq!(request["params"]["interactiveUser"], false); + let session_id = requested_session_id(&request).to_string(); + server_respond_create(&mut server_write, &request, &session_id).await; + timeout(TIMEOUT, create_handle).await.unwrap().unwrap(); + + let resume_handle = tokio::spawn({ + let client = client.clone(); + let session_id = session_id.clone(); + async move { + client + .resume_session( + ResumeSessionConfig::new(SessionId::from(session_id)) + .with_interactive_user(false), + ) + .await + .unwrap() + } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "session.resume"); + assert_eq!(request["params"]["interactiveUser"], false); + server_respond_create(&mut server_write, &request, &session_id).await; + respond_to_reload(&mut server_read, &mut server_write).await; + timeout(TIMEOUT, resume_handle).await.unwrap().unwrap(); +} + #[test] fn managed_settings_resolved_event_preserves_client_provenance() { let sources = [ @@ -5549,6 +5600,77 @@ async fn elicitation_requested_dispatches_to_handler_and_responds() { assert_eq!(rpc_call["params"]["result"]["content"]["name"], "Alice"); } +#[tokio::test] +async fn elicitation_requested_forwards_the_wire_schema_verbatim() { + use github_copilot_sdk::types::ElicitationResult; + + struct CaptureHandler(std::sync::Mutex>); + #[async_trait] + impl ElicitationHandler for CaptureHandler { + async fn handle( + &self, + _session_id: SessionId, + _request_id: RequestId, + request: ElicitationRequest, + ) -> ElicitationResult { + *self.0.lock().unwrap() = request.requested_schema; + ElicitationResult { + action: "cancel".to_string(), + content: None, + } + } + } + + let handler = Arc::new(CaptureHandler(std::sync::Mutex::new(None))); + let (_session, mut server) = create_session_pair_with_config({ + let handler = handler.clone(); + |cfg| cfg.with_elicitation_handler(handler) + }) + .await; + + let schema = serde_json::json!({ + "type": "object", + "title": "Deploy", + "properties": { + "service": { "type": "string", "title": "Service name" }, + "replicas": { "type": "integer", "minimum": 1 }, + "tier": { "type": "string", "enum": ["dev", "prod"] } + }, + "required": ["service"] + }); + server + .send_event( + "elicitation.requested", + serde_json::json!({ + "requestId": "elicit-verbatim", + "message": "Deploy settings", + "requestedSchema": schema.clone(), + "mode": "form", + }), + ) + .await; + + let rpc_call = timeout(TIMEOUT, server.read_request()).await.unwrap(); + assert_eq!(rpc_call["method"], "session.ui.handlePendingElicitation"); + let seen = handler + .0 + .lock() + .unwrap() + .clone() + .expect("handler saw a schema"); + assert_eq!(seen, schema); + // Map equality ignores order; assert the wire field order explicitly. + let keys = |v: &serde_json::Value| -> Vec { + v["properties"] + .as_object() + .unwrap() + .keys() + .cloned() + .collect() + }; + assert_eq!(keys(&seen), ["service", "replicas", "tier"]); +} + #[tokio::test] async fn elicitation_requested_cancels_on_handler_error() { struct FailHandler; @@ -7296,6 +7418,104 @@ async fn hooks_invoke_subagent_lifecycle_does_not_warn_as_unknown() { ); } +/// The CLI sends the `error` of the `errorOccurred` and `sessionEnd` hook +/// inputs as an error object. Both inputs reach their hooks with the object's +/// `message`, and no hook dispatch fails. +#[tokio::test] +async fn hooks_invoke_decodes_the_clis_error_objects() { + use github_copilot_sdk::hooks::{ + ErrorOccurredInput, ErrorOccurredOutput, HookContext, SessionEndInput, SessionEndOutput, + SessionHooks, + }; + + #[derive(Default)] + struct Recorder { + errors: std::sync::Mutex>, + } + #[async_trait] + impl SessionHooks for Recorder { + async fn on_error_occurred( + &self, + input: ErrorOccurredInput, + _ctx: HookContext, + ) -> Option { + self.errors.lock().unwrap().push(input.error); + None + } + + async fn on_session_end( + &self, + input: SessionEndInput, + _ctx: HookContext, + ) -> Option { + self.errors + .lock() + .unwrap() + .push(input.error.unwrap_or_default()); + None + } + } + + let hooks = Arc::new(Recorder::default()); + let (capture, _guard) = capture_traces(); + let (_session, mut server) = create_session_pair_with_hooks(hooks.clone()).await; + + for (request_id, hook_type, input) in [ + ( + 305, + "errorOccurred", + serde_json::json!({ + "sessionId": server.session_id, + "timestamp": 1234567890, + "cwd": "/tmp", + "error": {"name": "Error", "message": "model timeout", "stack": "Error: model timeout"}, + "errorContext": "model_call", + "recoverable": true + }), + ), + ( + 306, + "sessionEnd", + serde_json::json!({ + "sessionId": server.session_id, + "timestamp": 1234567890, + "cwd": "/tmp", + "reason": "error", + "error": {"message": "retries exhausted"} + }), + ), + ] { + server + .send_request( + request_id, + "hooks.invoke", + serde_json::json!({ + "sessionId": server.session_id, + "hookType": hook_type, + "input": input + }), + ) + .await; + let response = timeout(TIMEOUT, server.read_response()).await.unwrap(); + assert_eq!(response["id"], request_id); + assert_eq!(response["result"], serde_json::json!({ "output": {} })); + } + + assert_eq!( + *hooks.errors.lock().unwrap(), + vec!["model timeout".to_string(), "retries exhausted".to_string()] + ); + let failures: Vec<_> = capture + .events + .lock() + .unwrap() + .iter() + .filter(|event| event.message_contains("hook dispatch failed")) + .cloned() + .collect(); + assert!(failures.is_empty(), "hook dispatch failures: {failures:?}"); +} + async fn create_session_pair_with_system_message_transforms( transforms: Arc, ) -> (github_copilot_sdk::session::Session, FakeServer) { 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..a4cabea1b0 100644 --- a/scripts/codegen/go.ts +++ b/scripts/codegen/go.ts @@ -578,6 +578,7 @@ interface GoCodegenCtx { encodingBlocks?: Set; unionVariantMarshalers?: Set; packageName?: string; + numberPreservingTypes?: ReadonlySet; } function extractGoEventVariants(schema: JSONSchema7): GoEventVariant[] { @@ -1142,11 +1143,27 @@ function registerGoExternalUnionUnmarshalers( } } +function pushGoNumberPreservingUnmarshal(typeName: string, ctx: GoCodegenCtx): void { + pushGoEncodingBlock([ + `func (r *${typeName}) UnmarshalJSON(data []byte) error {`, + `\ttype wire ${typeName}`, + `\tdecoder := json.NewDecoder(bytes.NewReader(data))`, + `\tdecoder.UseNumber()`, + `\treturn decoder.Decode((*wire)(r))`, + `}`, + ], ctx); +} + function pushGoStructUnmarshalJSON(lines: string[], typeName: string, fields: GoStructField[], ctx: GoCodegenCtx): void { const unionFields = fields .map((field) => ({ field, unionField: goDiscriminatedUnionField(field.goType, ctx) })) .filter((entry): entry is { field: GoStructField; unionField: GoDiscriminatedUnionField } => entry.unionField !== undefined); - if (unionFields.length === 0) return; + const preservesNumbers = ctx.numberPreservingTypes?.has(typeName) === true + && fields.some((field) => /\bany\b/.test(field.goType)); + if (unionFields.length === 0) { + if (preservesNumbers) pushGoNumberPreservingUnmarshal(typeName, ctx); + return; + } const blockLines: string[] = []; blockLines.push(`func (r *${typeName}) UnmarshalJSON(data []byte) error {`); @@ -1161,7 +1178,11 @@ function pushGoStructUnmarshalJSON(lines: string[], typeName: string, fields: Go } blockLines.push(`\t}`); blockLines.push(`\tvar raw raw${typeName}`); - blockLines.push(`\tif err := json.Unmarshal(data, &raw); err != nil {`); + if (preservesNumbers) { + blockLines.push(`\tdecoder := json.NewDecoder(bytes.NewReader(data))`); + blockLines.push(`\tdecoder.UseNumber()`); + } + blockLines.push(`\tif err := ${preservesNumbers ? "decoder.Decode(&raw)" : "json.Unmarshal(data, &raw)"}; err != nil {`); blockLines.push(`\t\treturn err`); blockLines.push(`\t}`); @@ -3013,8 +3034,12 @@ function emitGoAlias(typeName: string, schema: JSONSchema7, ctx: GoCodegenCtx): if (isSchemaDeprecated(schema)) { pushGoCommentForContext(lines, `Deprecated: ${typeName} is deprecated and will be removed in a future version.`, ctx); } - lines.push(`type ${typeName} ${resolveGoPropertyType(schema, typeName, "Value", true, ctx)}`); + const goType = resolveGoPropertyType(schema, typeName, "Value", true, ctx); + lines.push(`type ${typeName} ${goType}`); ctx.structs.push(lines.join("\n")); + if (ctx.numberPreservingTypes?.has(typeName) && (goType.startsWith("map[") || goType.startsWith("[]")) && /\bany\b/.test(goType)) { + pushGoNumberPreservingUnmarshal(typeName, ctx); + } } /** @@ -3134,6 +3159,9 @@ function goGeneratedEncodingFileCode(schemaFileName: string, packageName: string lines.push(``); const imports = [`"encoding/json"`]; + if (generatedEncodingCode.includes("bytes.")) { + imports.push(`"bytes"`); + } if (generatedEncodingCode.includes("errors.")) { imports.push(`"errors"`); } @@ -3155,7 +3183,7 @@ function goGeneratedEncodingFileCode(schemaFileName: string, packageName: string return wrapComments ? wrapGeneratedGoComments(code) : code; } -function generateGoRpcTypeCode(definitions: Record, definitionCollections: DefinitionCollections): GoGeneratedTypeCode { +function generateGoRpcTypeCode(definitions: Record, definitionCollections: DefinitionCollections, numberPreservingTypes: ReadonlySet): GoGeneratedTypeCode { const ctx: GoCodegenCtx = { structs: [], encoding: [], @@ -3165,6 +3193,7 @@ function generateGoRpcTypeCode(definitions: Record, definit generatedNames: new Set(), definitions: definitionCollections, packageName: "rpc", + numberPreservingTypes, }; ctx.skipDefinitionTypeNames = collectGoDiscriminatedUnionVariantDefinitionTypeNames(definitions, ctx); const schemaKeysByTypeName = new Map(); @@ -4009,8 +4038,10 @@ async function generateRpc(schemaPath?: string): Promise { }; rpcDefinitions = allDefinitionCollections; + // Opt in only the policy result; existing RPC consumers depend on float64 values. + const numberPreservingTypes = new Set(["ContentExclusionValidatePoliciesResult"]); + const generatedRpcCode = generateGoRpcTypeCode(allDefinitions, allDefinitionCollections, numberPreservingTypes); // Strip trailing whitespace from generated output (gofmt requirement) - const generatedRpcCode = generateGoRpcTypeCode(allDefinitions, allDefinitionCollections); let generatedTypeCode = stripTrailingGoWhitespace(generatedRpcCode.typeCode); const generatedEncodingCode = stripTrailingGoWhitespace(generatedRpcCode.encodingCode); @@ -4090,9 +4121,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 +4335,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 +4394,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 a30a96b011..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) ───── @@ -3779,6 +3831,7 @@ import dateutil.parser T = TypeVar("T") EnumT = TypeVar("EnumT", bound=Enum) +UNSET_SENTINEL = object() `); lines.push(typesCode); @@ -3873,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. @@ -3889,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); } } @@ -4251,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"); @@ -4263,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 aad957837d..20b6f100d7 100644 --- a/scripts/codegen/rust.ts +++ b/scripts/codegen/rust.ts @@ -2522,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());`, ); @@ -2536,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/codegen/utils.ts b/scripts/codegen/utils.ts index eca2a34182..fe89663280 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -532,13 +532,13 @@ const PERMISSION_REQUEST_DEFINITION_NAMES = [ "PermissionRequestExtensionManagement", "PermissionRequestExtensionEnvAccess", "PermissionRequestExtensionPermissionAccess", - "PermissionRequestFactory", "PermissionRequestHook", "PermissionRequestMcp", "PermissionRequestMemory", "PermissionRequestRead", "PermissionRequestShell", "PermissionRequestUrl", + "PermissionRequestWorkflow", "PermissionRequestWrite", ] as const; 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..2d34b144f5 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( @@ -2317,89 +2343,116 @@ Related files (use view tool to read): } }); - test("matches shell tool results with shell ID completion markers", async () => { - const originalShellConfig = - process.platform === "win32" - ? ShellConfig.powerShell - : ShellConfig.bash; - const cachePath = path.join(tempDir, "cache.yaml"); - const cacheContent = yaml.stringify({ - models: ["test-model"], - conversations: [ - { - messages: [ - { role: "system", content: "${system}" }, - { role: "user", content: "Run command" }, - { - role: "assistant", - tool_calls: [ - { - id: "toolcall_0", - type: "function", - function: { - name: "${shell}", - arguments: '{"command":"echo ok"}', + test.each([ + { + toolName: "${shell}", + recorded: "ok\n", + actual: "ok\n", + }, + { + toolName: "${shell}", + recorded: "", + actual: "", + }, + { + toolName: "view", + recorded: "ok\n", + actual: "ok\n", + }, + { + toolName: "view", + recorded: "", + actual: "", + }, + ])( + "matches $toolName results containing shell markers: $actual", + async ({ toolName, recorded, actual }) => { + const originalShellConfig = + process.platform === "win32" + ? ShellConfig.powerShell + : ShellConfig.bash; + const cachePath = path.join(tempDir, "cache.yaml"); + const cacheContent = yaml.stringify({ + models: ["test-model"], + conversations: [ + { + messages: [ + { role: "system", content: "${system}" }, + { role: "user", content: "Run command" }, + { + role: "assistant", + tool_calls: [ + { + id: "toolcall_0", + type: "function", + function: { + name: toolName, + arguments: '{"command":"echo ok"}', + }, }, - }, - ], - }, - { - role: "tool", - tool_call_id: "toolcall_0", - content: "ok\n", - }, - { role: "assistant", content: "Done" }, - ], - }, - ], - } satisfies NormalizedData); - await writeFile(cachePath, cacheContent); + ], + }, + { + role: "tool", + tool_call_id: "toolcall_0", + content: recorded, + }, + { role: "assistant", content: "Done" }, + ], + }, + ], + } satisfies NormalizedData); + await writeFile(cachePath, cacheContent); - const proxy = new ReplayingCapiProxy( - "http://localhost:9999", - cachePath, - workDir, - ); - const proxyUrl = await proxy.start(); + const proxy = new ReplayingCapiProxy( + "http://localhost:9999", + cachePath, + workDir, + ); + const proxyUrl = await proxy.start(); - try { - const response = await makeRequest(proxyUrl, "/chat/completions", { - body: { - model: "test-model", - messages: [ - { role: "system", content: "System prompt" }, - { role: "user", content: "Run command" }, - { - role: "assistant", - tool_calls: [ - { - id: "runtime-call-id", - type: "function", - function: { - name: originalShellConfig.shellToolName, - arguments: '{"command":"echo ok"}', + try { + const response = await makeRequest(proxyUrl, "/chat/completions", { + body: { + model: "test-model", + messages: [ + { role: "system", content: "System prompt" }, + { role: "user", content: "Run command" }, + { + role: "assistant", + tool_calls: [ + { + id: "runtime-call-id", + type: "function", + function: { + name: + toolName === "${shell}" + ? originalShellConfig.shellToolName + : toolName, + arguments: '{"command":"echo ok"}', + }, }, - }, - ], - }, - { - role: "tool", - tool_call_id: "runtime-call-id", - content: "ok\n", - }, - ], - }, - }); + ], + }, + { + role: "tool", + tool_call_id: "runtime-call-id", + content: actual, + }, + ], + }, + }); - expect(response.status).toBe(200); - expect( - (JSON.parse(response.body) as ChatCompletion).choices[0].message - .content, - ).toBe("Done"); - } finally { - await proxy.stop(); - } - }); + expect(response.status).toBe(200); + expect( + (JSON.parse(response.body) as ChatCompletion).choices[0].message + .content, + ).toBe("Done"); + } finally { + await proxy.stop(); + } + }, + ); test("matches semantically equivalent interrupted tool results", async () => { const originalShellConfig = diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index ad21f0d317..8496173e6a 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" @@ -1820,8 +1829,17 @@ function coalesceMessages( // older CLI versions as well as to live requests. function normalizeStoredToolMessages(conversations: NormalizedConversation[]) { for (const conversation of conversations) { + const shellToolCallIds = new Set(); for (const message of conversation.messages) { + for (const toolCall of message.tool_calls ?? []) { + if (toolCall.function?.name === "${shell}") { + shellToolCallIds.add(toolCall.id); + } + } if (message.role === "tool" && typeof message.content === "string") { + if (message.tool_call_id && shellToolCallIds.has(message.tool_call_id)) { + message.content = normalizeShellExitMarkers(message.content); + } message.content = normalizeInterruptedToolResult(message.content); message.content = normalizeAvailableToolNames(message.content); message.content = normalizeBackgroundAgentStartMessage(message.content); @@ -1860,10 +1878,15 @@ function normalizeLargeOutputFilepaths(result: string): string { } function normalizeShellExitMarkers(result: string): string { - return result.replace( - /\r\n]+?\s+completed with exit code (-?\d+)>/g, - "", - ); + return result + .replace( + /\r\n]+?\s+completed with exit code (-?\d+)>/g, + "", + ) + .replace( + //g, + "", + ); } // The `gh` CLI emits different "not authenticated" help text depending on the 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/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/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-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/mode_handlers/should_invoke_exit_plan_mode_handler_when_model_uses_tool.yaml b/test/snapshots/mode_handlers/should_invoke_exit_plan_mode_handler_when_model_uses_tool.yaml index ba9e2c5cea..ea3638ee21 100644 --- a/test/snapshots/mode_handlers/should_invoke_exit_plan_mode_handler_when_model_uses_tool.yaml +++ b/test/snapshots/mode_handlers/should_invoke_exit_plan_mode_handler_when_model_uses_tool.yaml @@ -20,6 +20,8 @@ conversations: You are now in interactive mode. Start implementing the plan now, in this same response. Approving the plan is - your go-signal, so do not stop to ask whether to proceed or wait for another message. + your go-signal, so do not stop to ask whether to proceed or wait for another message. Do NOT call + task_complete or claim the task is done until you have actually implemented the plan — approving the plan is + not the same as completing it. - role: assistant content: The greeting file implementation plan was approved. diff --git a/test/snapshots/objective_lifecycle/reduces_goal_completion_before_idle_and_pauses_a_goal_when_leaving_autopilot.yaml b/test/snapshots/objective_lifecycle/reduces_goal_completion_before_idle_and_pauses_a_goal_when_leaving_autopilot.yaml new file mode 100644 index 0000000000..03c6847d02 --- /dev/null +++ b/test/snapshots/objective_lifecycle/reduces_goal_completion_before_idle_and_pauses_a_goal_when_leaving_autopilot.yaml @@ -0,0 +1,32 @@ +models: + - claude-sonnet-5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: >- + The user set this explicit autopilot objective with /autopilot: + + + Call task_complete immediately with summary 'The release is ready.' Do not call other tools or perform any + work. + + + Work autonomously toward this objective in clear checkpoints. + + Before acting, identify the success condition and the validation loop needed to prove progress. + + Keep making scoped progress until the objective is fully achieved, blocked by a real issue, or the user + changes the objective. + + Only call task_complete when the objective is fully achieved and verified. + + Do not call task_complete if more implementation, validation, or cleanup remains. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: task_complete + arguments: '{"summary":"The release is ready."}' diff --git a/test/snapshots/pending_work_resume/should_continue_an_approved_permission_after_abandoning_an_earlier_pending_permission.yaml b/test/snapshots/pending_work_resume/should_continue_an_approved_permission_after_abandoning_an_earlier_pending_permission.yaml new file mode 100644 index 0000000000..dac85d274f --- /dev/null +++ b/test/snapshots/pending_work_resume/should_continue_an_approved_permission_after_abandoning_an_earlier_pending_permission.yaml @@ -0,0 +1,58 @@ +models: + - claude-sonnet-5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Use resume_permission_tool with value 'alpha', then reply with the result. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: resume_permission_tool + arguments: '{"value":"alpha"}' + - role: tool + tool_call_id: toolcall_0 + content: Tool execution was interrupted before a result was recorded. Its effects may be absent, partial, or already + complete, and other tools may have completed successfully. Check the current state before retrying; do not + assume success or replay this operation blindly. + - role: user + content: Use resume_permission_tool with value 'beta', then reply with the result. + - role: assistant + tool_calls: + - id: toolcall_1 + type: function + function: + name: resume_permission_tool + arguments: '{"value":"beta"}' + - messages: + - role: system + content: ${system} + - role: user + content: Use resume_permission_tool with value 'alpha', then reply with the result. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: resume_permission_tool + arguments: '{"value":"alpha"}' + - role: tool + tool_call_id: toolcall_0 + content: The execution of this tool, or a previous tool was interrupted. + - role: user + content: Use resume_permission_tool with value 'beta', then reply with the result. + - role: assistant + tool_calls: + - id: toolcall_1 + type: function + function: + name: resume_permission_tool + arguments: '{"value":"beta"}' + - role: tool + tool_call_id: toolcall_1 + content: PERMISSION_RESUMED_BETA + - role: assistant + content: "Result: PERMISSION_RESUMED_BETA" diff --git a/test/snapshots/pending_work_resume/should_keep_pending_external_tool_handleable_on_cold_resume_when_continuependingwork_is_false.yaml b/test/snapshots/pending_work_resume/should_keep_pending_external_tool_handleable_on_cold_resume_when_continuependingwork_is_false.yaml index d8ffe5df61..3393a1ce5a 100644 --- a/test/snapshots/pending_work_resume/should_keep_pending_external_tool_handleable_on_cold_resume_when_continuependingwork_is_false.yaml +++ b/test/snapshots/pending_work_resume/should_keep_pending_external_tool_handleable_on_cold_resume_when_continuependingwork_is_false.yaml @@ -15,7 +15,9 @@ conversations: arguments: '{"value":"beta"}' - role: tool tool_call_id: toolcall_0 - content: The execution of this tool, or a previous tool was interrupted. + content: Tool execution was interrupted before a result was recorded. Its effects may be absent, partial, or already + complete, and other tools may have completed successfully. Check the current state before retrying; do not + assume success or replay this operation blindly. - role: user content: "Reply with exactly: COLD_RESUMED_FOLLOWUP" - role: assistant diff --git a/test/snapshots/pending_work_resume/should_preserve_a_completed_siblings_result_on_cold_resume.yaml b/test/snapshots/pending_work_resume/should_preserve_a_completed_siblings_result_on_cold_resume.yaml index 92b31e143f..48795ca386 100644 --- a/test/snapshots/pending_work_resume/should_preserve_a_completed_siblings_result_on_cold_resume.yaml +++ b/test/snapshots/pending_work_resume/should_preserve_a_completed_siblings_result_on_cold_resume.yaml @@ -1,6 +1,25 @@ models: - claude-sonnet-5 conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Call pending_lookup_a with value 'alpha' and pending_lookup_b with value 'beta', then reply with both results. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: pending_lookup_a + arguments: '{"value":"alpha"}' + - role: assistant + tool_calls: + - id: toolcall_1 + type: function + function: + name: pending_lookup_b + arguments: '{"value":"beta"}' - messages: - role: system content: ${system} @@ -23,8 +42,11 @@ conversations: content: PARALLEL_A_ALPHA - role: tool tool_call_id: toolcall_1 - content: The execution of this tool, or a previous tool was interrupted. + content: Tool execution was interrupted before a result was recorded. Its effects may be absent, partial, or already + complete, and other tools may have completed successfully. Check the current state before retrying; do not + assume success or replay this operation blindly. - role: user - content: "Use the completed lookup result and report it without re-running pending_lookup_a. Reply with exactly: COLD_RESUMED_A_RETAINED" + content: "Use the completed lookup result and report it without re-running pending_lookup_a. Reply with exactly: + COLD_RESUMED_A_RETAINED" - role: assistant content: COLD_RESUMED_A_RETAINED 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/plan_fleet_approval/should_deliver_fleet_instructions_with_the_approved_plan_tool_result.yaml b/test/snapshots/plan_fleet_approval/should_deliver_fleet_instructions_with_the_approved_plan_tool_result.yaml index 25525331ae..bae47c6707 100644 --- a/test/snapshots/plan_fleet_approval/should_deliver_fleet_instructions_with_the_approved_plan_tool_result.yaml +++ b/test/snapshots/plan_fleet_approval/should_deliver_fleet_instructions_with_the_approved_plan_tool_result.yaml @@ -22,8 +22,10 @@ conversations: Plan approved! Exited plan mode. - You are now in autopilot mode with fleet enabled. Proceed with implementing the plan in fleet mode by - following the fleet-mode instructions below. + You are now in autopilot mode with fleet enabled. Proceed with implementing the plan now in fleet mode by + following the fleet-mode instructions below. Do NOT call task_complete or report the task as finished until + the plan has genuinely been implemented and verified. Creating or approving the plan does not count as + completing the task. 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/scenario_testing_callbacks/should_approve_scenario_exit_plan_with_full_callback_and_event_state.yaml b/test/snapshots/scenario_testing_callbacks/should_approve_scenario_exit_plan_with_full_callback_and_event_state.yaml index 11a5eb48c6..16e52d6fed 100644 --- a/test/snapshots/scenario_testing_callbacks/should_approve_scenario_exit_plan_with_full_callback_and_event_state.yaml +++ b/test/snapshots/scenario_testing_callbacks/should_approve_scenario_exit_plan_with_full_callback_and_event_state.yaml @@ -21,6 +21,8 @@ conversations: You are now in interactive mode. Start implementing the plan now, in this same response. Approving the plan is - your go-signal, so do not stop to ask whether to proceed or wait for another message. + your go-signal, so do not stop to ask whether to proceed or wait for another message. Do NOT call + task_complete or claim the task is done until you have actually implemented the plan — approving the plan is + not the same as completing it. - role: assistant content: The scenario client plan was approved.